Bash kabuğundaki $ {var}, "$ var" ve "$ {var}" arasındaki fark nedir?


135

Ne başlık diyor: o bir değişkeni kapsülleyen ne demek oluyor {}, ""ya "{}?" Bu konuda çevrimiçi herhangi bir açıklama bulmak mümkün olmamıştır - Ben, semboller kullanarak hariç onlara başvurmak için edemedik hangi hiçbir şey vermez.

İşte bir örnek:

declare -a groups

groups+=("CN=exampleexample,OU=exampleexample,OU=exampleexample,DC=example,DC=com")
groups+=("CN=example example,OU=example example,OU=example example,DC=example,DC=com")

Bu:

for group in "${groups[@]}"; do
    echo $group
done

Bundan çok daha farklı olduğunu kanıtlıyor:

for group in $groups; do
    echo $group
done

ve bu:

for group in ${groups}; do
    echo $group
done

Yalnızca ilki istediğimi başarır: dizideki her bir öğeyi yinelemek. Ben arasındaki farklar hakkında gerçekten net değilim $groups, "$groups", ${groups}ve "${groups}". Açıklayabilecek biri varsa çok sevinirim.

Ek bir soru olarak - bu kapsüllemelere atıfta bulunmanın kabul edilen yolunu bilen var mı?


Yanıtlar:


229

Diş telleri ( $varvs. ${var})

Çoğu durumda $varve ${var}aynıdır:

var=foo
echo $var
# foo
echo ${var}
# foo

Kaşlı ayraçlar yalnızca ifadelerdeki belirsizliği çözmek için gereklidir:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

Alıntılar ( $varvs "$var"vs "${var}")

Bir değişkenin etrafına çift tırnak işareti eklediğinizde, kabuğa beyaz boşluklar içerse bile onu tek bir kelime olarak ele almasını söylersiniz:

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

Bu davranışı aşağıdakilerle karşılaştırın:

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

Olduğu gibi $varvs. ${var}, parantez sadece örneğin belirsizliği azaltmak için gereklidir:

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

O Not "${var}bar"İkinci örnekte yukarıda da yazılmış olabilir "${var}"bar, bu durumda, yani artık parantez gerek yok "$var"bar. Bununla birlikte, dizenizde çok sayıda alıntı varsa, bu alternatif formların okunması zorlaşabilir (ve bu nedenle bakımı zor olabilir). Bu sayfa Bash'de alıntı yapmaya iyi bir giriş sağlar.

Diziler ( $varvs $var[@]vs ${var[@]})

Şimdi diziniz için. Göre bash kılavuzu :

Bir dizi değişkenine alt simge olmadan başvurmak, diziye 0 alt simge ile başvurmakla eşdeğerdir.

Başka bir deyişle, bir dizin sağlamazsanız [], dizinin ilk öğesini elde edersiniz:

foo=(a b c)
echo $foo
# a

Tam olarak aynı olan

foo=(a b c)
echo ${foo}
# a

Bir dizinin tüm elemanlarını elde etmek için @, indeks olarak kullanmanız gerekir , örn ${foo[@]}. Dizilerde parantezler gereklidir, çünkü bunlar olmadan kabuk önce $fooparçayı genişleterek dizinin ilk öğesini ve ardından bir değişmez değeri verir [@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

Bu sayfa Bash'teki dizilere iyi bir giriş niteliğindedir.

Yeniden ziyaret edilen teklifler ( ${foo[@]}vs. "${foo[@]}")

Bunu sormadınız ama bilmeniz iyi olan ince bir fark. Dizinizdeki öğeler boşluk içerebiliyorsa, her öğenin ayrı bir "kelime" olarak ele alınması için çift tırnak kullanmanız gerekir:

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

Bunu çift tırnak içermeyen davranışla karşılaştırın:

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second

3
${var:?}Değişken ayarlanmadığında veya ayarlanmadığında hata verecek başka bir durum daha var . REF: github.com/koalaman/shellcheck/wiki/SC2154
Nam Nguyen

4
Eğer diğer formlarına bahsetmek istiyorum @NamNguyen parametre genişletme : daha en az bir düzine vardır ${parameter:-word}, ${parameter:=word}, ${parameter#word}, ${parameter/pattern/string}, vb. Bence bunlar bu cevabın kapsamı dışında.
ThisSuitIsBlackNot

Aslında, çift tırnak tartışması biraz eksiktir. Daha fazla stackoverflow.com/questions/10067266/… bakın
17:20, üçlü

11

TL; DR

Verdiğiniz tüm örnekler Bash Shell Expansions'daki varyasyonlardır . Genişletmeler belirli bir sırada gerçekleşir ve bazılarının özel kullanım durumları vardır.

Token Sınırlayıcıları Olarak Küme Ayraçları

${var}Sözdizimi esas olarak belirsiz belirteçleri sınırlandırılması için kullanılır. Örneğin, şunları düşünün:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

Dizi Genişletmelerinde Kaşlı Ayraçlar

Bir dizinin öğelerine erişmek için parantezler gereklidir ve diğer özel genişletmelere . Örneğin:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

dizgeciklere

Geri kalan sorularınızın çoğu, alıntı yapmakla ve kabuğun nasıl girdi oluşturduğuyla ilgilidir. Aşağıdaki örneklerde kabuğun kelime bölme işlemini nasıl gerçekleştirdiğine ilişkin farkı düşünün :

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

@Sembol farklı alıntı ile etkileşime girer *. özellikle:

  1. $@ "[e] xpands konumsal parametrelere, birden başlayarak. Genişleme çift tırnak içinde gerçekleştiğinde, her parametre ayrı bir kelimeye genişler."
  2. Bir dizide, "[i] f kelime çift tırnaklıdır, ${name[*]} IFS değişkeninin ilk karakteriyle ayrılmış her bir dizi üyesinin değeri ile tek bir kelimeye ${name[@]}genişler ve ismin her öğesini ayrı bir kelimeye genişletir."

Bunu eylem halinde aşağıdaki gibi görebilirsiniz:

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

Değişkenler boşluklar veya özel karakterler içeren değerlere atıfta bulunduğunda, kabuğun istediğiniz şekilde sözcük bölmesini engelleyebilecek alıntılanmış bir genişletmenin kullanılması büyük önem taşır. Bash'de alıntı yapmanın nasıl çalıştığı hakkında daha fazla bilgi için Alıntı yapma konusuna bakın .


7

Diziler ve basit değişkenler arasında ayrım yapmanız gerekir - ve sizin örneğiniz bir dizi kullanıyor.

Düz değişkenler için:

  • $var ve ${var}tam olarak eşdeğerdir.
  • "$var"ve "${var}"tam olarak eşdeğerdir.

Bununla birlikte, iki çift her durumda% 100 aynı değildir. Aşağıdaki çıktıyı düşünün:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

Değişkenin etrafındaki çift tırnaklar olmadan, iç boşluk kaybolur ve genişletme, printfkomuta iki argüman olarak kabul edilir . Değişkenin etrafındaki çift tırnaklarla, iç boşluk korunur ve genişletme, printfkomut için bir argüman olarak değerlendirilir .

Dizilerde kurallar hem benzer hem de farklıdır.

  • Bir groupsdiziyse, başvuran $groupsveya başvuruyla ${groups}aynı ${groups[0]}değerdeyse, dizinin sıfırıncı öğesi.
  • Referans verme "${groups[@]}", referans vermeye benzer"$@" ; dizinin tek tek öğelerindeki aralığı korur ve dizinin öğesi başına bir değer olacak şekilde bir değerler listesi döndürür.
  • Referans ${groups[@]}çift tırnak işareti olmadan aralığı korumaz ve elemanların bazıları boşluklar içeriyorsa dizinin elemanları vardır daha fazla değer neden olabilir.

Örneğin:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

*Bunun yerine kullanmak @ince farklı sonuçlara yol açar.

Ayrıca bkz . Bir bashkomut dosyasındaki bağımsız değişkenler üzerinde nasıl yineleme yapılır .


3

Altındaki ilk paragrafın ikinci cümlesi Parametre Genişleme in man bashder

Genişletilecek parametre adı veya sembolü, isteğe bağlı olan, ancak genişletilecek değişkeni, adın bir parçası olarak yorumlanabilen, hemen ardından gelen karakterlerden korumaya yarayan parantez içine alınabilir.

Bu size adın sadece parantez olduğunu ve asıl amacın adın nerede başlayıp nerede bittiğini açıklığa kavuşturmak olduğunu söyler :

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

Daha fazlasını okursanız keşfedersiniz,

Parametre birden fazla basamaklı bir konum parametresi olduğunda küme parantezleri gereklidir…

Hadi test edelim:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

Huh. Temiz. Bunu yazmadan önce dürüstçe bilmiyordum (Daha önce hiç 9'dan fazla konumsal parametreye sahip olmadım.)

Elbette, güçlü parametre genişletme özelliklerini yapmak için parantezlere de ihtiyacınız var.

${parameter:-word}
${parameter:=word}
${parameter:?word}
 [read the section for more]

yanı sıra dizi genişletme.


3

Yukarıda ele alınmayan ilgili bir vaka. Boş bir değişkenden alıntı yapmak işleri değiştiriyor gibi görünüyor test -n. Bu, infometinde özellikle örnek olarak verilmiştir coreutils, ancak tam olarak açıklanmamıştır:

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

Ayrıntılı açıklamayı duymak isterim. Testim bunu onaylıyor ve şimdi aynı sonucu almaktan kaçınmak -zve -ndöndürmek için tüm dizgi testleri için değişkenlerimi alıntılıyorum .

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better

2

Bir değişkenin kapsüllenmesinin size aşağıdaki gibi bir şeyle çalışmanıza yardımcı olduğunu biliyorum:

${groups%example}

veya böyle bir sözdizimi, değeri döndürmeden önce değişkeninizle bir şeyler yapmak istediğiniz yerde.

Şimdi, eğer kodunuzu görürseniz, tüm sihir içeride

${groups[@]}

sihir orada çünkü sadece yazamazsın: $groups[@]

Değişkeninizi içine koyuyorsunuz {}çünkü özel karakterler kullanmak istiyorsunuz []ve @. Değişkeninizi yalnızca: @veya something[]bunlar diğer işlemler ve adlar için ayrılmış karakterler oldukları için adlandıramaz veya çağıramazsınız .


Bu, çift tırnakların çok önemli anlamını ve onlarsız kodun temelde nasıl kırıldığını göstermez.
2017
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.