Tırnaksız boşluklarla parametre genişletme neden “[[” çift parantez içinde çalışıyor ancak “[” tek parantez içinde değil?


85

Tek veya çift parantez kullanmakla karıştırıldım. Şu koda bak:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

Dize bir boşluk içeriyor olmasına rağmen mükemmel çalışıyor. Ama onu tek bir dirseğe değiştirdiğimde:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

Diyor ki:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

Bunu değiştirdiğimde:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

İyi çalışıyor. Birisi neler olduğunu açıklayabilir mi? "${var}"Boşlukların neden olduğu sorunları önlemek gibi değişkenlerin etrafına çift tırnak işareti atamam gerekir mi?



Yanıtlar:


83

Tek parantez [aslında testkomut için bir takma addır, sözdizimi değildir .

Tek desteğin dezavantajlarından biri (birçoğunun) tek işlenenin bir ya da daha fazlası boş bir dize döndürmeyi değerlendirmek istiyorsa, iki işlenen beklediğinden (ikili) şikayet edeceğidir. Eğer insanların yaptığı görmenizin nedeni budur [ x$foo = x$blah ], xişlenen boş bir dizeye değerlendirmek asla garantiler.

Çift dirsek [[ ]], diğer taraftan, sözdizimi ve daha çok yetenekli [ ]. Belirttiğiniz gibi, tek bir işlenen sorunu yok ve aynı zamanda >, <, >=, <=, !=, ==, &&, ||işleçlerle daha fazla C benzeri sözdizimi sağlıyor .

Benim tavsiyem şudur: Tercümanınız ise #!/bin/bash, her zaman kullanın[[ ]]

O dikkat etmek önemlidir [[ ]]tüm POSIX kabukları tarafından desteklenmemektedir, ancak birçok kabukları gibi onu destekliyorsunuz zshve kshek olarakbash


16
Boş dize (ve diğer birçok) sorun tırnak işaretleri kullanılarak çözülmüştür. "X" bir başka problemi de kapsar: işlenenlerin işleç olarak alınabileceği yerler . Gibi zaman $fooolduğu !ya (ya -n... Sorun argüman sayısı (yanında POSIX kabukları ile ilgili bir sorun anlamına gelmez [ve ]) büyük olmayan dört aşıyor.
Stéphane Chazelas

21
Netleştirmek için, [ x$foo = x$blah ]aynı derecede yanlış [ $foo = $bar ]. [ "$foo" = "$bar" ]POSIX uyumlu herhangi bir kabuğun içinde doğrudur, herhangi [ "x$foo" = "x$bar" ]bir Bourne benzeri kabuğun içinde çalışır, ancak xboş dizelerin olduğu $foodurumlar için değil ama nerede olabilir !ya da -n...
Stéphane Chazelas

1
Bu cevaptaki ilginç anlamlar. Ne demek istediğini bilmiyorum * değil * sözdizimi . Elbette, [tam olarak bir takma ad değil test. Olsaydı, o testzaman bir kapanış köşeli ayraç kabul ederdi. test -n foo ]. Ama testdeğil ve bir tane [gerektirir. diğer tüm yönleriyle [aynıdır test, ancak bu şekilde çalışmaz alias. Bash açıklar [ve testolarak kabuk yerleşikleri ama [[bir şekilde kabuk anahtar kelime .
kojiro

1
Eh, bash [bir kabuk yerleşik, ancak / usr / bin / [da çalıştırılabilir. Geleneksel olarak / usr / bin / test bağlantısıdır, ancak modern gnu coreutils'te ayrı bir ikili dosya bulunur. Testin geleneksel versiyonu argv [0] 'ı [[] olarak çağrılıp çağrılmadığını görmek için inceler ve ardından eşleşmeyi arar.
Evan

Benim bash çalışmıyor >=ve<=
Öğrenci,

51

[Komut sıradan bir komuttur. Çoğu kabuk onu verimlilik için yerleşik olarak sağlamasına rağmen, kabuğun normal sözdizimsel kurallarına uyar. son argümanını gerektirdiği halde gerektirmeyen durumlar dışında [tamamen eşdeğerdir .test[]test

İkili ayraçlar [[ … ]]özel bir sözdizimidir. Birkaç yıl sonra ksh ile tanıtıldılar [çünkü [doğru kullanımı zor olabilir ve [[kabuk özel karakterleri kullanan bazı yeni eklemelere izin verir. Mesela yazabilirsiniz

[[ $x = foo && $y = bar ]]

Tüm koşullu ifade kabuk tarafından ayrıştırılır, çünkü ise [ $x = foo && $y = bar ]ilk iki komutları ürüne ayrılır [ $x = foove $y = bar ]ayrılmış &&operatör. Benzer şekilde çift parantez, örüntü eşleştirme sözdizimi gibi şeyleri mümkün kılar; örneğin [[ $x == a* ]], xbaşlangıç değeri a; tek parantez içinde bu a*, isimleri ageçerli dizinde başlayan dosyaların listesine genişleyecektir . Çift parantezler önce ksh ile tanıtıldı ve sadece ksh, bash ve zsh olarak kullanılabiliyor.

Tek parantez içinde, diğer birçok yerde olduğu gibi değişken değişkenler etrafında çift tırnak kullanmanız gerekir, çünkü bunlar yalnızca bir komutun argümanlarıdır (komut olur [). İkili parantez içinde, çift tırnak işaretine ihtiyacınız yoktur, çünkü kabuk sözcük bölme ya da globbing yapmaz: şartlı bir ifadeyi ayrıştırır, bir komutu değil.

Bir istisna olsa, [[ $var1 = "$var2" ]]bayt baytlık bir dize karşılaştırması yapmak istiyorsanız, alıntılara ihtiyaç duyduğunuz yerde, aksi halde $var2eşleşecek bir kalıp olur $var1.

Yapamayacağınız bir şey [[ … ]]operatör olarak değişken kullanmaktır. Örneğin, bu tamamen yasaldır (ancak nadiren kullanışlıdır):

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

Senin örneğinde

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

içeride komut ifise [4 argümanlarla -d, /home/mazimi/VirtualBox, VMsve ]. Kabuk ayrıştırır -d /home/mazimi/VirtualBoxve sonra ne yapacağını bilemez VMs. İyi ${dir}biçimlendirilmiş bir komut almak için kelimelerin ayrılmasını önlemeniz gerekir .

Genel olarak konuşursak, sonuç üzerinde kelime bölme ve konuşma yapmak istemediğiniz sürece değişken ve komut değişimlerinin etrafında her zaman çift tırnak işareti kullanın. Çift tırnak kullanmamanın güvenli olduğu ana yerler:

  • bir ödevde: foo=$bar(fakat bunun export "foo=$bar"gibi bir dizi ödevinde çift ​​tırnak işaretine ihtiyacınız olduğunu unutmayın array=("$a" "$b"));
  • bir caseaçıklamada case $foo in …:;
  • Sağ taraftaki dışında çift ayraç içindeki =veya ==: operatörü (eğer desen eşleştirme istiyorsun sürece) [[ $x = "$y" ]].

Bunların hepsinde, çift tırnak kullanmak doğrudur, bu yüzden gelişmiş kuralları atlayıp tırnakları her zaman kullanabilirsiniz.


1
@StephaneChazelas Benim bir hatam. Aslında [1981’de Sistem III’ten , daha eski olduğunu düşündüm.
Gilles

8

"${var}"Boşlukların neden olduğu sorunları önlemek gibi değişkenlerin etrafına çift tırnak işareti atamam gerekir mi?

Bu sorudaki örtük

Neden yeterince iyi değil?${variable_name}

${variable_name} ne düşündüğün anlamına gelmiyor…

... Eğer sahip düşünüyorsanız şey (değişken değerleri) boşluklarla kaynaklanan sorunları ortadan kaldırmak. bunun için iyidir:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

ve başka hiçbir şey! 1  ,değişken adının bir parçası olabilecek bir karakterle hemen izlediğiniz sürece hiçbir yararıolmaz: bir harf (-veya-), alt çizgi () veya rakam (-). Ve o zaman bile, bunun üzerinde çalışabilirsiniz:${variable_name}AZaz_09

$ echo "$bar"d
food

Kullanımını caydırmaya çalışmıyorum - echo "${bar}d"muhtemelen buradaki en iyi çözümdür - ancak insanların tırnaklar yerine ayraçlara güvenmelerini ya da içgüdüsel olarak ayraç kullanmasını ve sonra “Şimdi, ayrıca tırnaklara ihtiyacım var mı? “  Olmaması için iyi bir nedeniniz yoksa ve ne yaptığınızı bildiğinizden emin olduğunuzdan daima alıntı kullanmalısınız.
_________________
1    hariç tabii ki, daha süslü biçimleri gerçeği parametre genişleme , örneğin, ve , üzerine inşa sözdizimi. Ayrıca, kullanmak gerekir , 10, 11., vb konumsal parametreler referans, vb - tırnak bu konuda size yardımcı olmayacaktır.${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}


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.