Różnica między pojedynczymi i podwójnymi nawiasami kwadratowymi w Bash

161

Czytam przykłady basha, ifale niektóre przykłady są zapisane w pojedynczych nawiasach kwadratowych:

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

inne z podwójnymi nawiasami kwadratowymi:

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

Jaka jest różnica?

rkmax
źródło
3
Możesz uzyskać odpowiedź, patrząc na odpowiedź na to pytanie: unix.stackexchange.com/questions/3831/…
Amir Naghizadeh

Odpowiedzi:

188

Pojedyncze []to testy stanu zgodne z powłoką POSIX.

Double [[]]są rozszerzeniem standardu []i są obsługiwane przez bash i inne powłoki (np. Zsh, ksh). Obsługują dodatkowe operacje (jak również standardowe operacje Posix). Na przykład: ||zamiast -oi dopasowywanie wyrażeń regularnych z =~. Pełniejszą listę różnic można znaleźć w sekcji podręcznika basha poświęconej konstrukcjom warunkowym .

Używaj []zawsze, gdy chcesz, aby twój skrypt był przenoszony przez powłoki. Użyj, [[]]jeśli chcesz, aby wyrażenia warunkowe nie były obsługiwane przez []i nie muszą być przenośne.

cmh
źródło
6
Dodałbym, że jeśli twój skrypt nie zaczyna się od shebang, który wyraźnie żąda powłoki, która obsługuje [[ ]](np. Bash z #!/bin/bashlub #!/usr/bin/env bash), powinieneś użyć opcji przenośnej. Skrypty, które zakładają, że / bin / sh obsługuje takie rozszerzenia, zepsują się w systemach operacyjnych, takich jak ostatnie wydania Debiana i Ubuntu, w których tak nie jest.
Gordon Davisson
87

Różnice w zachowaniu

Testowane w Bash 4.3.11:

  • Rozszerzenie POSIX vs Bash:

  • zwykłe polecenie kontra magia

    • [ to zwykłe polecenie o dziwnej nazwie.

      ]jest po prostu argumentem, [który uniemożliwia użycie dalszych argumentów.

      Ubuntu 16.04 ma w rzeczywistości plik wykonywalny /usr/bin/[dostarczony przez coreutils, ale wersja wbudowana bash ma pierwszeństwo.

      Nic nie jest zmieniane w sposobie, w jaki Bash analizuje polecenie.

      W szczególności <jest przekierowaniem &&i ||konkatenacją wielu poleceń, ( )generuje podpowłoki, chyba że zostanie przed nimi zmieniona \, a interpretacja słów odbywa się jak zwykle.

    • [[ X ]]jest pojedynczą konstrukcją, która umożliwia Xmagiczne przetwarzanie. <, &&, ||I ()są traktowane specjalnie, oraz zasady podziału na słowa są różne.

      Istnieją również dalsze różnice, takie jak =i =~.

      W Bashese: [jest poleceniem wbudowanym i [[jest słowem kluczowym: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && i ||

    • [[ a = a && b = b ]]: prawda, logiczne i
    • [ a = a && b = b ]: błąd składni, &&analizowany jako separator polecenia ANDcmd1 && cmd2
    • [ a = a -a b = b ]: odpowiednik, ale przestarzały przez POSIX³
    • [ a = a ] && [ b = b ]: POSIX i niezawodny odpowiednik
  • (

    • [[ (a = a || a = b) && a = b ]]: fałszywe
    • [ ( a = a ) ]: błąd składni, ()jest interpretowany jako podpowłoka
    • [ \( a = a -o a = b \) -a a = b ]: odpowiednik, ale ()jest przestarzały przez POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]Odpowiednik POSIX 5
  • podział na słowa i generowanie nazw plików po rozwinięciu (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: prawda, cytaty nie są potrzebne
    • x='a b'; [ $x = 'a b' ]: błąd składni, rozwija się do [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: błąd składni, jeśli w bieżącym katalogu jest więcej niż jeden plik.
    • x='a b'; [ "$x" = 'a b' ]: Odpowiednik POSIX
  • =

    • [[ ab = a? ]]: prawda, ponieważ dopasowuje wzorce ( * ? [są magiczne). Nie rozwija globalnie do plików w bieżącym katalogu.
    • [ ab = a? ]: a?glob się rozszerza. Więc może być prawda lub fałsz w zależności od plików w bieżącym katalogu.
    • [ ab = a\? ]: false, a nie globalna ekspansja
    • =i ==są takie same w obu [i [[, ale ==jest rozszerzeniem Bash.
    • case ab in (a?) echo match; esac: Odpowiednik POSIX
    • [[ ab =~ 'ab?' ]]: false 4 , traci magię z''
    • [[ ab? =~ 'ab?' ]]: prawdziwe
  • =~

    • [[ ab =~ ab? ]]: prawda, dopasowanie rozszerzonego wyrażenia regularnego POSIX , ?nie rozwija się globalnie
    • [ a =~ a ]: błąd składni. Brak odpowiednika basha.
    • printf 'ab\n' | grep -Eq 'ab?': Odpowiednik POSIX (tylko dane jednowierszowe)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Odpowiednik POSIX.

Zalecenie : zawsze używaj [].

Istnieją odpowiedniki POSIX dla każdej [[ ]]konstrukcji, którą widziałem.

Jeśli używasz [[ ]]ciebie:

  • stracić przenośność
  • zmusić czytelnika do poznania zawiłości innego rozszerzenia bash. [jest zwykłym poleceniem o dziwnej nazwie, bez specjalnej semantyki.

¹ Zainspirowany równoważną [[...]]konstrukcją w powłoce Korna

² ale kończy się niepowodzeniem w przypadku niektórych wartości alub b(takich jak +lub index) i porównuje liczbowo, jeśli ai bwygląda jak dziesiętne liczby całkowite. expr "x$a" '<' "x$b"działa wokół obu.

³ i również zawodzi w przypadku niektórych wartości alub bpodobnych !lub (.

4 w bash 3.2 i nowszych i pod warunkiem, że kompatybilność z bash 3.1 nie jest włączona (jak w BASH_COMPAT=3.1)

5 chociaż grupowanie (tutaj z {...;}grupą poleceń zamiast (...)której uruchomiłaby niepotrzebną podpowłokę) nie jest konieczne, ponieważ operatory ||i &&powłoki (w przeciwieństwie do operatorów ||i && [[...]]lub operatorów -o/ -a [) mają równy priorytet. Więc [ a = a ] || [ a = b ] && [ a = b ]byłoby równoważne.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
3
Istnieją odpowiedniki POSIX dla każdego konstruktu [[]], jaki widziałem. ” To samo można powiedzieć o każdym języku Turing Complete na powierzchni planety.
A. Rick,
3
@ A.Rick, to byłaby poprawna odpowiedź na wszystkie pytania typu „Jak zrobić X w języku Y” :-) Oczywiście, w tym stwierdzeniu jest ukryta „wygodna”.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6
Fantastyczne podsumowanie. Dzięki za wysiłek. Nie zgadzam się jednak z zaleceniem. Jest to przenośność w porównaniu z bardziej spójną i potężną składnią. Jeśli możesz wymagać bash> 4 w swoich środowiskach, zalecana jest składnia [[]].
Bernard
@Downvoters proszę wyjaśnić, abym mógł się uczyć i ulepszać informacje :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
8
Świetna odpowiedź, ale myślę, że Zalecenie : zawsze używaj[] należy odczytywać jako Moje preferencje : używaj, []jeśli nie chcesz stracić przenośności . Jak stwierdzono tutaj : Jeśli problemem jest przenośność / zgodność z POSIX lub BourneShell, należy użyć starej składni. Jeśli z drugiej strony skrypt wymaga BASH, Zsh lub KornShell, nowa składnia jest zwykle bardziej elastyczna, ale niekoniecznie kompatybilna wstecz. Wolę pójść z, [[ ab =~ ab? ]]jeśli mogę i nie martwię się o kompatybilność wsteczną niżprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo
14

W nawiasach pojedynczych dla testu warunków (tj. [...]), niektóre operatory, takie jak single, =są obsługiwane przez wszystkie powłoki, podczas gdy użycie operatora ==nie jest obsługiwane przez niektóre starsze powłoki.

Wewnątrz podwójnych nawiasów do badania stanu (tj. [...]]) nie ma różnicy między użyciem =lub ==w starych lub nowych powłokach.

Edycja: Powinienem również zauważyć, że: w bash zawsze używaj podwójnych nawiasów [...]], jeśli to możliwe, ponieważ jest to bezpieczniejsze niż pojedyncze. Zilustruję dlaczego na następującym przykładzie:

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

jeśli zmienna $ var ma wartość null / jest pusta, skrypt widzi to:

if [ == "hello" ]; then

co zepsuje twój skrypt. Rozwiązaniem jest użycie podwójnych nawiasów kwadratowych lub zawsze pamiętaj o umieszczaniu zmiennych w cudzysłowie ( "$var"). Podwójne nawiasy to lepsza metoda obronnego kodowania.

sampson-chen
źródło
1
Umieszczanie cudzysłowów wokół wszystkich odczytów zmiennych, chyba że masz bardzo dobry powód, by tego nie robić, jest znacznie lepszą obronną praktyką kodowania, ponieważ dotyczy wszystkich odczytów zmiennych, a nie tylko tych w warunkach. Błąd instalatora iTunes po usunięciu plików ludzi, jeśli nazwa dysku twardego zawierała spacje (lub coś w tym rodzaju). Rozwiązuje również wspomniany problem.
Chai T. Rex
1

możesz użyć podwójnych nawiasów kwadratowych do lekkiego dopasowywania wyrażeń regularnych, np .:

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

(o ile wersja basha, której używasz, obsługuje tę składnię)

asf107
źródło
6
Z wyjątkiem tego, że podałeś cytowany wzorzec, więc jest teraz traktowany jako dosłowny ciąg.
ormaaj
bardzo prawdziwe. czasami to mnie denerwuje :)
asf107
1

Instrukcja Bash mówi:

W przypadku użycia z [[, operatory „<” i „>” sortują leksykograficznie przy użyciu bieżących ustawień regionalnych. Polecenie test używa kolejności ASCII.

(Polecenie testowe jest takie samo jak [])

user5500105
źródło