Jaka jest różnica między operatorami Bash [[vs [vs (vs ((?


243

Jestem trochę zdezorientowany, co te operatory robią inaczej, gdy są używane w bash (nawiasy, podwójne nawiasy, nawiasy i podwójne nawiasy).

[[ , [ , ( , ((

Widziałem, jak ludzie używają ich, jeśli takie stwierdzenia:

if [[condition]]

if [condition]

if ((condition))

if (condition)

4
Możesz najpierw spojrzeć na unix.stackexchange.com/questions/tagged/test
cuonglm


3
@cuonglm Ironic, ponieważ ten link daje to pytanie jako pierwszy wynik. Paradoks!
Szalony

5
Przypuszczam, że czytanie dokumentacji nie jest opcją?
Wyścigi lekkości na orbicie

34
Nawiasy i nawiasy nie są tak łatwe do wyszukania w dokumentacji, a to wszystko, co masz, jeśli nie znasz nazw tych funkcji.
ilkkachu

Odpowiedzi:


255

ifOświadczenie zazwyczaj wygląda

if commands1
then
   commands2
else
   commands3
fi

thenKlauzula jest wykonywany, jeżeli kod wyjścia commands1wynosi zero. Jeśli kod wyjścia jest niezerowy, elseklauzula jest wykonywana. commands1może być prosty lub złożony. To może być, na przykład, sekwencji z jednej lub więcej rur, oddzielonych przez jedną z operatorów ;, &, &&lub ||. Przedstawione ifponiżej warunki to tylko szczególne przypadki commands1:

  1. if [ condition ]

    Jest to tradycyjne testpolecenie powłoki . Jest dostępny we wszystkich powłokach POSIX. Polecenie testowe ustawia kod wyjścia, a ifinstrukcja działa odpowiednio. Typowe testy sprawdzają, czy plik istnieje, czy jedna liczba jest równa drugiej.

  2. if [[ condition ]]

    Jest to nowa ulepszona odmiana testod ksh, która obsługuje także bash i zsh . To testpolecenie ustawia także kod wyjścia, a ifinstrukcja działa odpowiednio. Wśród rozszerzonych funkcji może sprawdzać, czy łańcuch pasuje do wyrażenia regularnego.

  3. if ((condition))

    Kolejne rozszerzenie ksh, które obsługuje także bash i zsh . Wykonuje arytmetykę. W wyniku arytmetyki ustawiany jest kod wyjścia i ifinstrukcja działa odpowiednio. Zwraca kod wyjścia równy zero (prawda), jeśli wynik obliczeń arytmetycznych jest niezerowy. Na przykład [[...]]ten formularz nie jest POSIX i dlatego nie jest przenośny.

  4. if (command)

    Uruchamia to polecenie w podpowłoce. Po zakończeniu komendy ustawia kod wyjścia, a ifinstrukcja działa odpowiednio.

    Typowym powodem korzystania podpowłoce jak to jest ograniczenie skutków ubocznych command, jeśli commandwymagane zmienne zadania lub inne zmiany w środowisku powłoki. Takie zmiany nie pozostaną po zakończeniu podpowłoki.

  5. if command

    polecenie jest wykonywane, a ifinstrukcja działa zgodnie z kodem wyjścia.


24
Dziękujemy za włączenie piątej opcji. To klucz do zrozumienia, jak to naprawdę działa i jest zaskakująco niewykorzystane.
pisklęta

4
Zauważ, że [tak naprawdę jest to binarne, a nie wewnętrzne polecenie lub symbol. Na ogół mieszka w /bin.
Julien R.

8
@JulienR. tak naprawdę [jest wbudowany test. Dostępne są wersje binarne ze względu na kompatybilność. Sprawdź help [i help test.
OldTimer

4
Warto zauważyć, że podczas gdy ((nie jest POSIX, $((tzn. Rozszerzenie arytmetyki jest i łatwo je pomylić. Często można obejść ten [ $((2+2)) -eq 4 ]problem, używając arytmetyki w instrukcjach warunkowych
Sergiy Kolodyazhnyy

Jak się bawić drukowania nowe linie: if echo; then echo; fi.
AnthonyD973

77
  • (…)nawiasy oznaczają podpowłokę . To, co jest w nich, nie jest wyrażeniem jak w wielu innych językach. To lista poleceń (podobnie jak poza nawiasami). Te polecenia są wykonywane w osobnym podprocesie, więc żadne przekierowanie, przypisanie itp. Wykonane w nawiasach nie ma wpływu poza nawiasami.
    • Z wiodącym znakiem dolara $(…)jest podstawienie polecenia : w nawiasach znajduje się polecenie, a dane wyjściowe polecenia są używane jako część wiersza polecenia (po dodatkowych rozwinięciach, chyba że podstawienie występuje między podwójnymi cudzysłowami, ale to inna historia ) .
  • { … }nawiasy klamrowe są jak nawiasy, ponieważ grupują polecenia, ale wpływają tylko na parsowanie, a nie na grupowanie. Program x=2; { x=4; }; echo $xwypisuje 4, podczas gdy x=2; (x=4); echo $xwypisuje 2. (Nawiasy klamrowe będące słowami kluczowymi należy oddzielić i znaleźć w pozycji polecenia (stąd spacja za {i ;przed }), podczas gdy nawiasy nie. To tylko dziwactwo składniowe.)
    • Z wiodącym znakiem dolara, ${VAR}jest rozszerzeniem parametru , rozwijającym się do wartości zmiennej, z możliwymi dodatkowymi przekształceniami. ksh93Powłoka wspomaga również ${ cmd;}w postaci podstawienia poleceń, które nie tarło podpowłokę.
  • ((…))podwójne nawiasy otaczają instrukcję arytmetyczną , czyli obliczanie liczb całkowitych, ze składnią podobną do innych języków programowania. Ta składnia jest najczęściej używana do przypisań i warunków. To istnieje tylko w ksh / bash / zsh, a nie w zwykłym sh.
    • Ta sama składnia jest używana w wyrażeniach arytmetycznych $((…)), które rozwijają się do wartości całkowitej wyrażenia.
  • [ … ]pojedyncze nawiasy otaczają wyrażenia warunkowe . Wyrażenia warunkowe są w większości oparte na operatorach, takich jak -n "$variable"testowanie, czy zmienna jest pusta i -e "$file"testowanie, czy plik istnieje. Pamiętaj, że potrzebujesz spacji wokół każdego operatora (np. [ "$x" = "$y" ]Nie [ "$x"="$y" ]) oraz spacji lub znaku, takiego ;jak wewnątrz i na zewnątrz nawiasów kwadratowych (np. [ -n "$foo" ]Nie [-n "$foo"]).
  • [[ … ]]podwójne nawiasy kwadratowe są alternatywną formą wyrażeń warunkowych w ksh / bash / zsh z kilkoma dodatkowymi funkcjami, na przykład możesz napisać, [[ -L $file && -f $file ]]aby sprawdzić, czy plik jest dowiązaniem symbolicznym do zwykłego pliku, podczas gdy pojedyncze nawiasy wymagają [ -L "$file" ] && [ -f "$file" ]. Zobacz Dlaczego interpretacja parametrów ze spacjami bez cudzysłowów działa w podwójnych nawiasach [[ale nie w pojedynczych nawiasach [? więcej na ten temat.

W powłoce każde polecenie jest poleceniem warunkowym: każde polecenie ma status zwracany, który wynosi 0, co oznacza sukces lub liczbę całkowitą od 1 do 255 (i potencjalnie więcej w niektórych powłokach), co wskazuje na niepowodzenie. [ … ]Polecenie (lub [[ … ]]forma składni) jest określone polecenie, które mogą być pisane test …i uda, jeśli istnieją plików, lub gdy łańcuch nie jest pusty lub, gdy liczba jest mniejsza niż druga itp ((…))postać składni udaje się, gdy liczba jest niezerowy. Oto kilka przykładów warunkowych w skrypcie powłoki:

  • Sprawdź, czy myfilezawiera ciąg hello:

    if grep -q hello myfile; then 
  • Jeśli mydirjest to katalog, przejdź do niego i wykonaj następujące czynności:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • Sprawdź, czy myfilew bieżącym katalogu znajduje się plik :

    if [ -e myfile ]; then 
  • To samo, ale także z wiszącymi dowiązaniami symbolicznymi:

    if [ -e myfile ] || [ -L myfile ]; then 
  • Sprawdź, czy wartość x(która przyjmuje się, że jest liczbowa) wynosi co najmniej 2, przenośnie:

    if [ "$x" -ge 2 ]; then 
  • Sprawdź, czy wartość x(która przyjmuje się, że jest liczbowa) wynosi co najmniej 2, w bash / ksh / zsh:

    if ((x >= 2)); then 

Zauważ, że pojedynczy nawias obsługuje -azamiast &&, więc można napisać:, [ -L $file -a -f $file ]która jest taką samą liczbą znaków w nawiasach bez dodatkowych [i ]...
Alexis Wilke

6
@AlexisWilke Operatory -ai -osą problematyczne, ponieważ mogą prowadzić do niepoprawnych analiz, jeśli niektóre z operandów wyglądają jak operatory. Dlatego ich nie wspominam: mają zerową przewagę i nie zawsze działają. I nigdy nie pisz niecytowanych rozszerzeń zmiennych bez uzasadnionego powodu: [[ -L $file -a -f $file ]]jest w porządku, ale potrzebujesz pojedynczych nawiasów [ -L "$file" -a -f "$file" ](co jest dobre, np. Jeśli $filezawsze zaczyna się od /lub ./).
Gilles

Zauważ, że jest [[ -L $file && -f $file ]](nie -az [[...]]wariantem).
Stéphane Chazelas

18

Z dokumentacji bash :

(list)lista jest wykonywana w środowisku podpowłoki (patrz ŚRODOWISKO WYKONYWANIA POLECEŃ poniżej). Zmienne przypisania i wbudowane polecenia, które wpływają na środowisko powłoki, nie pozostają aktywne po zakończeniu polecenia. Status powrotu to status wyjścia z listy.

Innymi słowy, upewniasz się, że cokolwiek dzieje się na „liście” (jak a cd), nie ma wpływu poza (i ). Jedyną rzeczą, która będzie przeciekać jest kod zakończenia ostatniego polecenia lub z set -epierwszego polecenia, który generuje błąd (inne niż kilka, takich jak if, whileitp)

((expression))Wyrażenie ocenia się zgodnie z zasadami opisanymi poniżej w części OCENA ARYTETYCZNA. Jeśli wartość wyrażenia jest różna od zera, zwracanym statusem jest 0; w przeciwnym razie zwracany jest status 1. Jest to dokładnie równoważne z wyrażeniem „wyrażenie”.

To rozszerzenie bash pozwala na matematykę. Jest to nieco podobne do używania exprbez wszystkich ograniczeń expr(takich jak wszędzie spacje, ucieczka *itp.)

[[ expression ]]Zwraca status 0 lub 1 w zależności od oceny wyrażenia warunkowego. Wyrażenia składają się z liczb podstawowych opisanych poniżej w części WYRAŻENIA WARUNKOWE. Dzielenie wyrazów i interpretacja nazw ścieżek nie są wykonywane na słowach pomiędzy [[a]]; Wykonywane jest interpretacja tyldy, interpretacja parametrów i zmiennych, interpretacja arytmetyczna, podstawianie poleceń, podstawianie procesów i usuwanie cytatów. Operatory warunkowe, takie jak -f, muszą być niecytowane, aby można je było uznać za podstawowe.

W przypadku użycia z [[, operatory <i> sortują leksykograficznie przy użyciu bieżących ustawień regionalnych.

To oferuje zaawansowany test do porównywania ciągów, liczb i plików, trochę jak testoferty, ale bardziej wydajny.

[ expr ]Zwraca status 0 (prawda) lub 1 (fałsz) w zależności od oceny wyrażenia warunkowego wyraż. Każdy operator i oper musi być osobnym argumentem. Wyrażenia składają się z liczb podstawowych opisanych powyżej w części WYRAŻENIA WARUNKOWE. test nie akceptuje żadnych opcji, ani nie przyjmuje i ignoruje argumentu - jako oznaczającego koniec opcji.

[...]

Ten dzwoni test. W rzeczywistości w dawnych czasach [istniał symboliczny link do test. Działa tak samo i masz te same ograniczenia. Ponieważ plik binarny zna nazwę, z którą został uruchomiony, program testowy może analizować parametry, dopóki nie znajdzie parametru ]. Zabawne sztuczki uniksowe.

Należy pamiętać, że w przypadku bash, [i testsą wbudowane funkcje (jak wspomniano w komentarzu), jeszcze dość dużo obowiązują te same ograniczenia.


1
Chociaż testi [są oczywiście wbudowanymi poleceniami w Bash, ale prawdopodobnie istnieje również zewnętrzny plik binarny.
ilkkachu

1
Zewnętrzny plik binarny dla [nie jest dowiązaniem symbolicznym do testwiększości nowoczesnych systemów.
Random832

1
Zabawne jest dla mnie to, że starają się stworzyć dwa oddzielne pliki binarne, które mają dokładnie to, czego potrzebują, zamiast ich łączenia i dodawania kilku warunków. Chociaż faktycznie strings /usr/bin/testpokazuje, że ma także tekst pomocy, więc nie wiem, co powiedzieć.
ilkkachu

2
@ Random832 Rozumiem twoje uzasadnienie GNU, aby uniknąć nieoczekiwanego zachowania arg0, ale jeśli chodzi o wymagania POSIX, nie byłbym tak pozytywny. Chociaż testpolecenie to oczywiście musi istnieć jako samodzielne polecenie oparte na pliku przez standard, nic w nim nie stwierdza, że ​​jego [wariant również musi zostać zaimplementowany w ten sposób. Na przykład Solaris 11 nie zapewnia żadnych [plików wykonywalnych, ale mimo to jest w pełni zgodny ze standardami POSIX
jlliagre

2
(wyjście 1) działa poza nawiasami.
Alexander

14

[ vs [[

Ta odpowiedź obejmie [vs [[podzbiór kwestii.

Niektóre różnice w Bash 4.3.11:

  • Rozszerzenie POSIX vs Bash:

  • regularne dowodzenie kontra magia

    • [ to zwykłe polecenie o dziwnej nazwie.

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

      Ubuntu 16.04 faktycznie ma dla niego plik wykonywalny /usr/bin/[podany przez coreutils, ale wersja wbudowana bash ma pierwszeństwo.

      Nic nie zmienia sposobu, w jaki Bash analizuje polecenie.

      W szczególności <jest przekierowywaniem &&i ||łączeniem wielu poleceń, ( )generuje podpowłoki, chyba że jest to poprzedzone znakiem ucieczki \, a rozwijanie słów odbywa się jak zwykle.

    • [[ X ]]to pojedynczy konstrukt, który sprawia, że Xanalizuje się go magicznie. <, &&, ||I ()są traktowane specjalnie, oraz zasady podziału na słowa są różne.

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

      W języku bashese: [jest wbudowanym poleceniem i [[jest słowem kluczowym: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && i ||

    • [[ a = a && b = b ]]: prawda, logika i
    • [ a = a && b = b ]: błąd składniowy, &&analizowany jako separator poleceń ANDcmd1 && cmd2
    • [ a = a -a b = b ]: równoważne, ale przestarzałe przez POSIX³
    • [ a = a ] && [ b = b ]: POSIX i niezawodny odpowiednik
  • (

    • [[ (a = a || a = b) && a = b ]]: fałszywe
    • [ ( a = a ) ]: błąd składniowy, ()jest interpretowany jako podpowłoka
    • [ \( a = a -o a = b \) -a a = b ]: równoważne, ale ()jest przestarzałe przez POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]Odpowiednik POSIX 5
  • dzielenie słów i generowanie nazw plików po rozwinięciach (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: prawda, cytaty nie są potrzebne
    • x='a b'; [ $x = 'a b' ]: błąd składniowy, rozwija się do [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: błąd składniowy, 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 wzory ( * ? [są magiczne). Nie jest rozwijany globalnie do plików w bieżącym katalogu.
    • [ ab = a? ]: a?glob rozszerza się. Może być więc prawdą lub fałszem w zależności od plików w bieżącym katalogu.
    • [ ab = a\? ]: false, 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? ]]: true, rozszerzone dopasowanie wyrażeń regularnych POSIX , ?nie rozwija się globalnie
    • [ a =~ a ]: błąd składni. Brak odpowiednika bash.
    • 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żdego [[ ]]konstruktu, który widziałem.

Jeśli wykorzystasz [[ ]]:

  • stracić przenośność
  • zmusić czytelnika do poznania zawiłości innego rozszerzenia bash. [to zwykłe polecenie o dziwnej nazwie, nie wymaga specjalnej semantyki.

¹ Inspirowany równoważnym [[...]]konstruktem w skorupie Korna

², ale zawodzi w przypadku niektórych wartości alub b(jak +lub index) i dokonuje porównania numerycznego, jeśli ai bwygląda jak liczba całkowita dziesiętna. expr "x$a" '<' "x$b"działa w obu przypadkach.

³, a także zawodzi w przypadku niektórych wartości alub bpodobnych !lub (.

4 w wersji bash 3.2 i nowszej oraz pod warunkiem, że zgodność z wersją bash 3.1 nie jest włączona (jak w przypadku BASH_COMPAT=3.1)

5 chociaż grupowanie (tutaj z {...;}grupą komend zamiast (...)której uruchamiał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.


@ StéphaneChazelas dzięki za informacje! Dodałem exprdo odpowiedzi. Termin „rozszerzenie Bash” nie ma sugerować, że Bash był pierwszą powłoką, która dodała jakąś składnię. Uczenie się POSIX sh vs. Bash już wystarcza, by doprowadzić mnie do szaleństwa.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Sprawdź, man testczy próbowałeś man [i zgubiłeś się. To wyjaśni wariant POSIX.
Jonathan Komar

13

Kilka przykładów:

Tradycyjny test:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

testi [są komendami jak wszystkie inne, więc zmienna jest podzielona na słowa, chyba że jest w cudzysłowie.

Test w nowym stylu

[[ ... ]] jest (nowszą) specjalną konstrukcją powłoki, która działa nieco inaczej, najbardziej oczywistą rzeczą jest to, że nie dzieli zmiennych na słowa:

if [[ -n $foo ]] ; then... 

Trochę dokumentacji tutaj [i [[tutaj .

Test arytmetyczny:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

Polecenia „normalne”:

Wszystkie powyższe działają jak normalne polecenia i ifmogą przyjmować dowolne polecenia:

# grep returns true if it finds something
if grep pattern file ; then ...

Wiele poleceń:

Lub możemy użyć wielu poleceń. Zawijanie zestawu poleceń ( ... )uruchamia je w podpowłoce, tworząc tymczasową kopię stanu powłoki (katalog roboczy, zmienne). Jeśli musimy tymczasowo uruchomić jakiś program w innym katalogu:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...

1

Polecenia grupowania

Bash oferuje dwa sposoby grupowania listy poleceń do wykonania jako jednostka.

( list )Umieszczenie listy poleceń między nawiasami powoduje utworzenie środowiska podpowłoki i wykonanie każdego z poleceń na liście w tej podpowłoce. Ponieważ lista jest wykonywana w podpowłoce, przypisania zmiennych nie pozostają aktywne po zakończeniu podpowłoki.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }Umieszczenie listy poleceń między nawiasami klamrowymi powoduje, że lista jest wykonywana w bieżącym kontekście powłoki . Nie jest tworzona podpowłoka. Wymagana jest następująca lista średników (lub znaków nowej linii). Źródło

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Konstrukcje warunkowe

Pojedynczy nawias, tzn. []
Do porównania ==, !=, <,i >powinien być używany oraz do porównywania numerycznego eq, ne,lti gtpowinien być używany.

Ulepszone wsporniki tj[[]]

We wszystkich powyższych przykładach użyliśmy tylko pojedynczych nawiasów, aby zawrzeć wyrażenie warunkowe, ale bash pozwala na podwójne nawiasy, które służą jako ulepszona wersja składni pojedynczego nawiasu.

Dla porównania ==, !=, <,i >można używać dosłownie.

  • [jest synonimem polecenia testowego. Nawet jeśli jest wbudowany w powłokę, tworzy nowy proces.
  • [[ to nowa, ulepszona wersja, która jest słowem kluczowym, a nie programem.
  • [[jest rozumiany przez Korni Bash.

Źródło

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.