Bash'deki tek ve çift köşeli parantezler arasındaki fark


162

Hakkında bash örnekleri okuyorum ifama bazı örnekler tek köşeli parantez ile yazılmıştır:

if [ -f $param ]
then
  #...
fi

çift ​​köşeli parantez ile diğerleri:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Fark ne?


3
Cevabınızı şu sorunun cevabına bakarak alabilirsiniz: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Yanıtlar:


188

Tekil []posix kabuğu uyumlu durum testleridir.

İkili [[]], standardın bir uzantısıdır []ve bash ve diğer kabuklar (örn. Zsh, ksh) tarafından desteklenir. Ekstra işlemleri (standart posix işlemlerinin yanı sıra) desteklerler. Örneğin: ||yerine -ove regex eşleşmesi =~. Koşullu yapılarla ilgili bash manuel bölümünde daha farklı bir farklılık listesi bulunabilir .

[]Komut dosyanızın kabuklar arasında taşınabilir olmasını istediğinizde kullanın . Kullanım [[]]değil tarafından desteklenen koşullu ifadeleri istiyorsanız []ve yapmaması taşınabilir olması gerekir.


6
Senaryonuz açıkça destekleyen bir kabuk isteyen [[ ]](örneğin, bash ile #!/bin/bashveya #!/usr/bin/env bash), taşınabilir seçeneği kullanmanız gerektiğini ekleyeceğim . / Bin / sh komutunun bu tür uzantıları desteklediğini düşünen komut dosyaları, durumun böyle olmadığı son Debian ve Ubuntu sürümleri gibi işletim sistemlerini bozacaktır.
Gordon Davisson

87

Davranış farklılıkları

Bash 4.3.11'de test edilmiştir:

  • POSIX vs Bash uzantısı:

  • normal komut vs büyü

    • [ sadece garip bir isimle düzenli bir komut.

      ]sadece [başka bir argümanın kullanılmasını engelleyen bir argüman .

      Ubuntu 16.04 aslında /usr/bin/[coreutils tarafından sağlanan bir yürütülebilir dosyaya sahiptir , ancak bash yerleşik sürümü önceliklidir.

      Hiçbir şey Bash'in komutu ayrıştırma biçiminde değiştirilmez.

      Özellikle, <yeniden yönlendirme &&ve ||birden çok komutu birleştirmek, ( )kaçmadığı sürece alt kabuklar üretir \ve kelime genişletme her zamanki gibi gerçekleşir.

    • [[ X ]]Xsihirli bir şekilde ayrıştırılan tek bir yapıdır . <, &&, ||Ve ()özel olarak muamele edilmiş ve kelime bölme kuralları farklı olabilirler.

      =Ve gibi başka farklılıklar da vardır =~.

      Bashese'de: [yerleşik bir komuttur ve [[bir anahtar kelimedir: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && ve ||

    • [[ a = a && b = b ]]: doğru, mantıklı ve
    • [ a = a && b = b ]: sözdizimi hatası, &&AND komut ayırıcısı olarak ayrıştırıldıcmd1 && cmd2
    • [ a = a -a b = b ]: eşdeğer, ancak POSIX³ tarafından kullanımdan kaldırıldı
    • [ a = a ] && [ b = b ]: POSIX ve güvenilir eşdeğeri
  • (

    • [[ (a = a || a = b) && a = b ]]: yanlış
    • [ ( a = a ) ]: sözdizimi hatası, ()alt kabuk olarak yorumlanır
    • [ \( a = a -o a = b \) -a a = b ]: eşdeğer, ancak ()POSIX tarafından kullanımdan kaldırıldı
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX eşdeğeri 5
  • sözcük bölme ve genişletmeler üzerine dosya adı oluşturma (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: true, tırnak işaretleri gerekmez
    • x='a b'; [ $x = 'a b' ]: sözdizimi hatası, [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: geçerli dizinde birden fazla dosya varsa sözdizimi hatası.
    • x='a b'; [ "$x" = 'a b' ]: POSIX eşdeğeri
  • =

    • [[ ab = a? ]]: doğru, çünkü desen eşleşmesi yapıyor ( * ? [sihir). Geçerli dizindeki dosyalara genişlemez.
    • [ ab = a? ]: a?glob genişler. Bu nedenle, geçerli dizindeki dosyalara bağlı olarak doğru veya yanlış olabilir.
    • [ ab = a\? ]: yanlış, glob genişlemesi değil
    • =ve ==her ikisi de aynıdır [ve [[ancak ==bir Bash uzantısıdır.
    • case ab in (a?) echo match; esac: POSIX eşdeğeri
    • [[ ab =~ 'ab?' ]]: yanlış 4 , ile büyü kaybeder''
    • [[ ab? =~ 'ab?' ]]: doğru
  • =~

    • [[ ab =~ ab? ]]: doğru, POSIX genişletilmiş düzenli ifade eşleşmesi, ?genişlemeyi engellemez
    • [ a =~ a ]: sözdizimi hatası. Bash eşdeğeri yok.
    • printf 'ab\n' | grep -Eq 'ab?': POSIX eşdeğeri (yalnızca tek satırlı veriler)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX eşdeğeri.

Tavsiye : her zaman kullanın [].

[[ ]]Gördüğüm her yapı için POSIX eşdeğeri var .

Sizi kullanırsanız [[ ]]:

  • taşınabilirliği kaybetmek
  • okuyucuyu başka bir bash uzantısının inceliklerini öğrenmeye zorlayın. [garip bir isimle düzenli bir emirdir, özel anlambilim söz konusu değildir.

Orn [[...]]Korn kabuğundaki eşdeğer yapıdan esinlenmiştir

² ancak bazı değerler için başarısız aveya b(gibi +ya index) ve sayısal karşılaştırma eğer yapar ave bgöz gibi ondalık tamsayılar. expr "x$a" '<' "x$b"her ikisi de etrafında çalışır.

³ ve ayrıca bazı değerleri için başarısız aveya bbenzeri !veya (.

Bash 3.2 ve üzeri 4 ve bash 3.1 ile uyumluluk sağlanmamıştır (ile olduğu gibi BASH_COMPAT=3.1)

5 ve (burada {...;}yerine (...)gereksiz bir alt kabuk çalıştıracak olan komut grubu ile birlikte) gruplama gerekli değildir ||ve &&kabuk işleçleri ( ||ve && [[...]]işleçlerin veya -o/ -a [işleçlerin aksine ) eşit önceliğe sahiptir. Yani [ a = a ] || [ a = b ] && [ a = b ]eşdeğer olur.


3
" Gördüğüm her [[]] yapı için POSIX eşdeğeri var. " Aynı şey , gezegenin karşısındaki herhangi bir Turing Complete dili için de söylenebilir .
A. Rick

3
@ A. Bu "Y dilinde X nasıl yapılır" için geçerli bir cevap olacaktır.
Ciro Santilli 法轮功 10: 病 六四 事件 法轮功

6
Harika özet. Çaba için teşekkürler. Ancak tavsiye ile katılmıyorum. Daha tutarlı ve güçlü bir sözdizimine karşı taşınabilirliktir. Ortamlarınızda bash> 4'e gereksinim duyarsanız, [[]] sözdizimi önerilir.
Bernard

@Downvoters lütfen açıklamak böylece bilgi öğrenmek ve geliştirmek :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8
Harika bir yanıt ama bence Tavsiye : her zaman kullanım []benim tercihim olarak okunmalıdır : []taşınabilirliği kaybetmek istemiyorsanız kullanın . Belirtildiği gibi burada : POSIX veya BourneShell için taşınabilirlik / uygunluk bir sorun olacaksa, eski sözdizimi kullanılmalıdır. Diğer yandan komut dosyası BASH, Zsh veya KornShell gerektiriyorsa, yeni sözdizimi genellikle daha esnektir, ancak geriye dönük olarak uyumlu olmayabilir. [[ ab =~ ab? ]]Yapabileceğim ve geriye dönük uyumluluktan daha fazla endişe etmiyorsam tercih ederimprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo

14

Durum testi için tekli parantez içinde (yani [...]), single gibi bazı operatörler =tüm kabuklar tarafından desteklenirken, operatörün kullanımı ==eski kabukların bazıları tarafından desteklenmez.

Durumu deneyinde (yani [[...]]) için çift parantez içinde, kullanarak arasında hiçbir fark yoktur =ya da ==yeni ya da eski kabuklar.

Düzenleme: Ayrıca şunu da not etmeliyim: Bash'da, her zaman çift parantez [[...]] kullanın, çünkü tek parantezden daha güvenlidir. Nedenini aşağıdaki örnekle açıklayacağım:

if [ $var == "hello" ]; then

$ var boş / boş olursa, betiğin gördüğü budur:

if [ == "hello" ]; then

hangi senaryonuzu kıracak. Çözüm ya çift parantez kullanmak ya da değişkenlerinizin ( "$var") etrafına tırnak işaretleri koymayı unutmayın . Çift parantez daha iyi savunma kodlama uygulamasıdır.


1
Değişmemek için çok iyi bir nedeniniz olmadıkça, değişkenlerin tüm okumalarına alıntı yapmak çok daha iyi bir savunma kodlama uygulamasıdır, çünkü sadece koşullardaki değişkenler için değil, tüm değişken okumaları için geçerlidir. Bir iTunes yükleyici hatası, sabit sürücü adı boşluk içeriyorsa (veya bunun gibi bir şey) insanların dosyalarını sildi. Ayrıca bahsettiğiniz sorunu da çözer.
Chai T. Rex


1

ışık normal ifadesi eşleşmesi için çift köşeli parantezleri kullanabilirsiniz, örneğin:

if [[ $1 =~ "foo.*bar" ]] ; then

(Kullandığınız bash sürümü bu sözdizimini desteklediği sürece)


6
Deseni alıntılamanız dışında, şimdi değişmez bir dize olarak ele alınır.
ormaaj

çok doğru. bazen bu beni rahatsız ediyor :)
asf107 27:12

1

Bash kılavuzu diyor:

[[İle kullanıldığında, '<' ve '>' operatörleri geçerli yerel ayarı kullanarak sözlükbilimsel olarak sıralar. Test komutu ASCII sıralamasını kullanır.

(Test komutu [] ile aynıdır)

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.