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

] Nie wiem co robią te operatory inaczej, gdy używane w bash (nawiasy, podwójne nawiasy, nawiasy i podwójne nawiasy).

[[ , [ , ( , (( 

Widziałem ludzi używających ich w wyrażeniach if takich jak ta:

if [[condition]] if [condition] if ((condition)) if (condition) 

Komentarze

Odpowiedź

W powłokach typu Bournea instrukcja if zazwyczaj wygląda tak, jak

if command-list1 then command-list2 else command-list3 fi 

Klauzula then jest wykonywana, jeśli kod zakończenia lista poleceń wynosi zero. Jeśli kod zakończenia jest różny od zera, wykonywana jest klauzula else. command-list1 może być proste lub złożone. Może to być na przykład sekwencja jednego lub więcej potoków oddzielonych jednym z operatorów ;, &, &&, || lub nowa linia. Warunki if pokazane poniżej to tylko specjalne przypadki command-list1:

  1. if [ condition ]

    [ to inna nazwa tradycyjnego polecenia test. [ / test to standardowe narzędzie POSIX. Wszystkie powłoki POSIX mają to wbudowane (chociaż nie jest to wymagane przez POSIX²). Polecenie test ustawia kod zakończenia, a instrukcja if działa odpowiednio. Typowe testy sprawdzają, czy plik istnieje lub czy jedna liczba jest równa innej.

  2. if [[ condition ]]

    To jest nowa ulepszona odmiana test ¹ od ksh that bash , zsh , yash , busybox sh również obsługują. Ta konstrukcja [[ ... ]] ustawia również kod zakończenia, a if działa odpowiednio. Wśród swoich rozszerzonych funkcji może testować, czy ciąg pasuje do wzorca wieloznacznego (nie w busybox sh ).

  3. if ((condition))

    Kolejne rozszerzenie ksh , które obsługuje także bash i zsh . To wykonuje działania arytmetyczne. W wyniku arytmetyki ustawiany jest kod zakończenia, a instrukcja if działa zgodnie z rdingly. Zwraca kod zakończenia zerowy (prawda), jeśli wynik obliczenia arytmetycznego jest różny od zera. Podobnie jak [[...]], ten formularz nie jest POSIX i dlatego nie jest przenośny.

  4. if (command)

    To uruchamia polecenie w podpowłoce. Kiedy polecenie kończy działanie, ustawia kod zakończenia, a instrukcja if działa odpowiednio.

    Typowym powodem używania takiej podpowłoki jest ograniczenie skutków ubocznych command jeśli command wymagane przypisania zmiennych lub inne zmiany w środowisku powłoki. Takie zmiany nie pozostają po zakończeniu powłoki podpowłoki.

  5. if command

    polecenie jest wykonywane, a instrukcja if działa zgodnie z jego kodem zakończenia.


¹ chociaż nie jest to polecenie, ale specjalna konstrukcja powłoki z własną składnią oddzielną od zwykłego polecenia, i znacznie różni się między implementacjami powłoki

² POSIX wymaga, aby istniały samodzielne test i [ jednak w systemie, chociaż w przypadku [, kilka dystrybucji Linuksa było znanych jako m issing it.

Komentarze

  • Dziękujemy za dołączenie piątej opcji. To ' klucz do zrozumienia, jak to naprawdę działa, i jest zaskakująco niedostatecznie wykorzystywane.
  • Zwróć uwagę, że [ jest w rzeczywistości binarne, a nie wewnętrzne polecenie lub symbol. Zwykle mieszka w /bin.
  • @JulienR. w rzeczywistości [ jest wbudowane, podobnie jak test. Ze względu na kompatybilność dostępne są wersje binarne. Zobacz help [ i help test.
  • Warto zauważyć, że while ((isnt POSIX, $(( tj. interpretacja arytmetyczna jest i ' łatwo je pomylić. Często obejściem jest użycie czegoś takiego jak [ $((2+2)) -eq 4 ] używać arytmetyki w stwierdzeniach warunkowych
  • Chciałbym móc głosować za tą odpowiedzią więcej niż raz. Doskonałe wyjaśnienie.

Odpowiedź

  • (…) nawiasy oznaczają podpowłoka . To, co jest w nich, nie jest wyrażeniem, jak w wielu innych językach. Jest to lista poleceń (podobnie jak w nawiasach zewnętrznych). Polecenia te są wykonywane w oddzielnym podprocesie, więc wszelkie przekierowania, przypisania itp. Wykonywane wewnątrz nawiasów nie mają wpływu poza nawiasy. znak dolara, $(…) to podstawienie polecenia : w nawiasach znajduje się polecenie, a dane wyjściowe polecenia jest używany jako część wiersza poleceń (po dodatkowych rozszerzeniach, chyba że podstawienie następuje między podwójnymi cudzysłowami, ale to „s inna historia ).
  • { … } nawiasy klamrowe są jak nawiasy, ponieważ grupują polecenia, ale wpływają tylko na analizę, a nie na grupowanie. Program x=2; { x=4; }; echo $x wypisuje 4, podczas gdy x=2; (x=4); echo $x wypisuje 2. (Również nawiasy klamrowe będące słowami kluczowymi muszą być rozdzielone i znaleziono na pozycji polecenia (stąd spacja po { i ; przed }), podczas gdy w nawiasach nie ma „t. To tylko dziwactwo składniowe.)
    • Z początkowym znakiem dolara ${VAR} to rozwijanie parametrów , rozwijanie do wartości zmiennej, z możliwymi dodatkowymi przekształceniami. Powłoka ksh93 obsługuje również ${ cmd;} jako formę zastępowania poleceń, które nie „tworzy podpowłoki.
  • ((…)) podwójne nawiasy otaczają instrukcję arytmetyczną , czyli obliczenia na liczbach całkowitych, z składnia podobna do innych języków programowania. Ta składnia jest używana głównie do przypisań i warunków. 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 kwadratowe wyrażenia warunkowe . Wyrażenia warunkowe są w większości zbudowane na operatorach , takich jak -n "$variable", aby sprawdzić, czy zmienna jest pusta i -e "$file", aby sprawdzić, czy plik istnieje. Pamiętaj, że potrzebujesz spacji wokół każdej operator (np. [ "$x" = "$y" ], a nie [ "$x"="$y" ] ) i spacja lub znak, taki jak ; zarówno wewnątrz, jak i na zewnątrz nawiasów (np. [ -n "$foo" ], a nie [-n "$foo"] ).
  • [[ … ]] podwójne nawiasy to alternatywna forma 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 rozszerzanie parametrów ze spacjami bez cudzysłowów działa w podwójnych nawiasach kwadratowych [[ale nie w pojedynczych nawiasach [? ], aby uzyskać więcej informacji na ten temat.
  • W powłoce, każde polecenie jest poleceniem warunkowym: każde polecenie ma status powrotu, który jest albo 0 wskazujący na sukces, albo liczbą całkowitą od 1 do 255 (a potencjalnie więcej w niektórych powłokach) wskazującą niepowodzenie. Polecenie [ … ] (lub forma składni [[ … ]]) to szczególne polecenie, które można również zapisać jako test … i kończy się sukcesem, gdy plik istnieje, lub gdy ciąg nie jest pusty, lub gdy liczba jest mniejsza od innej itd. Forma składni ((…)) kończy się powodzeniem, gdy liczba jest różna od zera .Oto kilka przykładów warunków warunkowych w skrypcie powłoki:

    • Sprawdź, czy myfile zawiera ciąg hello:

      if grep -q hello myfile; then … 
    • Jeśli mydir jest katalogiem, zmień na to i rób rzeczy:

      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 istnieje plik o nazwie myfile w bieżącym katalogu:

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

      if [ -e myfile ] || [ -L myfile ]; then … 
    • Sprawdź, czy wartość x (przyjmowana jako numeryczna) wynosi co najmniej 2, przenośnie:

      if [ "$x" -ge 2 ]; then … 
    • Sprawdź, czy wartość x (przyjmowana jako liczba) wynosi co najmniej 2, w bash / ksh / zsh:

      if ((x >= 2)); then … 

    Komentarze

    • Zwróć uwagę, że pojedynczy nawias obsługuje -a zamiast &&, więc można napisać: [ -L $file -a -f $file ], czyli taką samą liczbę znaków w nawiasach bez dodatkowego [ i ]
    • @AlexisWilke Operatory -a i -o to problematyczne, ponieważ mogą prowadzić do niepoprawnych analiz, jeśli niektóre z operandów wyglądają jak operatory. To ' jest powodem, dla którego ' nie wspominam o nich: mają zerową przewagę i nie ' t zawsze działa. I nigdy nie pisz rozwinięć zmiennych bez cudzysłowów bez dobrego powodu: [[ -L $file -a -f $file ]] jest w porządku, ale z pojedynczymi nawiasami potrzebujesz [ -L "$file" -a -f "$file" ] (co jest w porządku, np. Jeśli $file zawsze zaczyna się od / lub ./).
    • Pamiętaj, że it ' s [[ -L $file && -f $file ]] (nie -a z [[...]] wariant).

    Odpowiedź

    [ vs [[

    Ta odpowiedź obejmie [ vs [[ podzbiór pytań.

    Niektóre różnice w Bash 4.3.11:

    • Rozszerzenie POSIX kontra Bash:

    • zwykłe polecenie a magia

      • [ to zwykłe polecenie o dziwnej nazwie.

        ] to tylko ostatni argument z [.

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

      Nic nie jest zmieniane w sposób, w jaki Bash analizuje polecenie.

      W szczególności < to przekierowanie, && i || łączą wiele poleceń, ( ) generuje podpowłok, chyba że są poprzedzone \, a interpretacja słów odbywa się jak zwykle.

      • [[ X ]] pojedyncza konstrukcja, która sprawia, że X jest analizowany w magiczny sposób. <, &&, || i () są traktowane specjalnie, a zasady dzielenia słów są inne.

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

      W Bashese: [ to wbudowane polecenie, a [[ to słowo kluczowe: https://askubuntu.com/questions/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, && przeanalizowano jako separator polecenia AND cmd1 && cmd2
      • [ a = a ] && [ b = b ]: niezawodny odpowiednik POSIX
      • [ a = a -a b = b ]: prawie równoważny, ale przestarzały przez POSIX, ponieważ jest szalony i kończy się niepowodzeniem dla niektórych wartości a lub b, takich jak ! lub ( co zostanie zinterpretowane jako operacje logiczne
    • (

      • [[ (a = a || a = b) && a = b ]]: false. Bez ( ) byłoby prawdziwe, ponieważ [[ && ]] ma wyższy priorytet niż [[ || ]]
      • [ ( a = a ) ]: błąd składni, () jest interpretowane jako podpowłoka
      • [ \( a = a -o a = b \) -a a = b ]: odpowiednik, ale (), -a i -o są wycofane przez POSIX. Bez \( \) byłoby prawdą, ponieważ -a ma wyższy priorytet niż -o
      • { [ a = a ] || [ a = b ]; } && [ a = b ] niezalecany odpowiednik POSIX. Jednak w tym konkretnym przypadku moglibyśmy napisać po prostu: [ a = a ] || [ a = b ] && [ a = b ], ponieważ || i && operatory powłoki mają równe pierwszeństwo w przeciwieństwie do [[ || ]] i [[ && ]] i -o, -a i [
    • dzielenie na słowa i generowanie nazw plików po rozwinięciu (split + glob )

      • x="a b"; [[ $x = "a b" ]]: prawda, cudzysłowy nie są potrzebne
      • x="a b"; [ $x = "a b" ]: składnia błąd, 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? ]]: true, ponieważ dopasowywanie wzorców ( * ? [ są magiczne). Nie rozwija się glob do f iles w bieżącym katalogu.
      • [ ab = a? ]: a? glob się rozwija. Więc może być prawda lub fałsz w zależności od plików w bieżącym katalogu.
      • [ ab = a\? ]: false, not glob expand
      • = i == są takie same w obu [ i [[, ale == to rozszerzenie Bash.
      • case ab in (a?) echo match; esac: odpowiednik POSIX
      • [[ ab =~ "ab?" ]]: false, traci magię z "" w Bash 3.2 i nowszych i pod warunkiem, że zgodność z bash 3.1 nie jest włączona (tak jak w przypadku BASH_COMPAT=3.1)
      • [[ ab? =~ "ab?" ]]: prawda
    • =~

      • [[ ab =~ ab? ]]: true, POSIX rozszerzone wyrażenie regularne pasuje, ? 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 []

    Dla każdego [[ ]] konstrukcji, które widziałem, istnieją odpowiedniki POSIX.

    Jeśli używasz [[ ]], to:

    • tracisz przenośność
    • zmusza czytelnika do poznania zawiłości innego rozszerzenia bash. [ to zwykłe polecenie o dziwnej nazwie, bez specjalnej semantyki.

    Dzięki Stéphane Chazelas w celu uzyskania ważnych poprawek i uzupełnień.

    Komentarze

    • @St é phaneChazelas dzięki za informacje! ' dodałem expr do odpowiedzi. Termin ” Bash ex napięcie ” nie ma sugerować, że Bash był pierwszą powłoką, która dodała trochę składni, nauka POSIX sh vs Bash już wystarczy, aby doprowadzić mnie do szaleństwa.
    • Zobacz man test, jeśli próbowałeś man [ i zgubiłeś się. To wyjaśni wariant POSIX.
    • Poprawka: ] jest argumentem polecenia [, ale nie ' t zapobiega używaniu dalszych argumentów. ] musi być ostatnim argumentem [, ale może również występować jako część wyrażenia testowego. Na przykład if [ "$foo" = ] ]; then sprawdzi, czy zmienna foo jest ustawiona na „] ” (tak jak if [ ] = "$foo" ]; then).
    • @GordonDavisson dzięki, nie ' wiem, naprawiono.
    • @ tgm1024 – Monicawasmistreated tak, to też jest ważna uwaga.

    Odpowiedź

    Z dokumentacji basha :

    Lista (list) jest wykonywana w środowisku podpowłoki (zobacz poniżej ŚRODOWISKO WYKONYWANIA POLECEŃ). Przypisania zmiennych i wbudowane polecenia, które wpływają na środowisko powłoki, nie działają po zakończeniu polecenia. Status powrotu to stan wyjścia z listy.

    Innymi słowy, upewniasz się, że cokolwiek dzieje się na „liście” (np. cd) nie ma żadnego efektu poza ( i ). Jedyne, co wycieknie, to kod zakończenia ostatniego polecenia lub z set -e pierwszym poleceniem, które generuje błąd (inne niż kilka, takich jak if, while itp.)

    ((expression)) Wyrażenie jest oceniane zgodnie z regułami opisanymi poniżej w OCENA ARYTMETYCZNA. Jeśli wartość wyrażenia jest różna od zera, zwracany jest status 0; w przeciwnym razie zwracany jest status wynosi 1. Jest to dokładnie równoważne z let ” wyrażenie „.

    To jest rozszerzenie basha, które pozwala wykonywać obliczenia matematyczne. Jest to trochę podobne do używania expr bez wszystkich ograniczeń expr (takich jak umieszczanie wszędzie spacji, znakowanie * itp.)

    [[ expression ]] Zwraca status 0 lub 1 w zależności od ocena wyrażenia warunkowego. Wyrażenia składają się z prawyborów opisanych poniżej w WYRAŻENIA WARUNKOWE. Dzielenie na słowa i rozwijanie nazw plików nie są wykonywane na słowach między [[i]]; wykonywane są interpretacja tyldy, interpretacja parametrów i zmiennych, interpretacja wyrażeń arytmetycznych, podstawianie poleceń, podstawianie procesów i usuwanie cytatów. Operatory warunkowe, takie jak -f, nie mogą być cytowane, aby były rozpoznawane jako prawybory.

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

    To oferuje zaawansowany test porównujący ciągi, liczby i pliki, takie jak test, ale o większej mocy.

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

    […]

    Ten wywołuje test. Właściwie w dawnych czasach [ był dowiązaniem symbolicznym do test. Działa w ten sam sposób i masz takie same ograniczenia. Ponieważ plik binarny zna nazwę, z jaką został uruchomiony, program testowy wie, kiedy został uruchomiony jako [ i może zignorować swój ostatni parametr, który powinien być ]. Zabawne sztuczki z Uniksem.

    Zauważ, że w przypadku bash, [ i test są funkcjami wbudowanymi (jak wspomniano w komentarzu), ale mają zastosowanie prawie te same ograniczenia.

    Komentarze

    • Chociaż test i [ to oczywiście polecenia wbudowane w Bash, ale ' prawdopodobnie zewnętrzny plik binarny też istnieje.
    • Zewnętrzny plik binarny dla [ nie jest dowiązaniem symbolicznym do test w większości nowoczesnych systemów .
    • W jakiś sposób wydaje mi się zabawne, że kłopoczą się utworzeniem dwóch oddzielnych plików binarnych, z których oba mają dokładnie to, czego potrzebują, zamiast po prostu łączyć je i dodawać kilka warunków. Chociaż w rzeczywistości strings /usr/bin/test pokazuje, że zawiera również tekst pomocy, więc nie ' nie wiem, co powiedzieć.
    • @ Random832 Rozumiem co do uzasadnienia GNU, aby uniknąć nieoczekiwanego zachowania arg0, ale co do wymagań POSIX, nie ' nie byłbym tak twierdzący. Chociaż polecenie test musi oczywiście istnieć jako samodzielne polecenie oparte na pliku przez standard, nic w nim nie stwierdza, że jego [ wariant musi być również wdrażane w ten sposób. Na przykład Solaris 11 nie ' nie zapewnia [ plików wykonywalnych, ale mimo to jest w pełni zgodny ze standardami POSIX
    • (wyjście 1) działa poza nawiasami.

    Odpowiedź

    Kilka przykładów:

    Test tradycyjny:

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

    test i [ to polecenia jak wszystkie inne, więc zmienna jest dzielona na słowa, chyba że znajduje się w cudzysłowie.

    Test w nowym stylu

    [[ ... ]] to (nowsza) specjalna konstrukcja powłoki, która działa nieco inaczej, najbardziej oczywistą rzeczą jest to, że nie obsługuje zmiennych dzielonych na słowa:

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

    Niektóre dokumentacja [ i [[ tutaj .

    Test arytmetyczny:

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

    „Normalny „polecenia:

    Wszystkie powyższe działają jak zwykłe polecenia, a if może przyjąć dowolne polecenie:

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

    Wiele poleceń:

    Lub możemy użyć wielu poleceń. Umieszczenie zestawu poleceń w ( ... ) uruchamia je w podpowłoce, tworząc tymczasową kopię stanu powłoki (katalog roboczy, zmienne). Jeśli potrzebujemy aby 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 ... 

    Odpowiedź

    Polecenia grupujące

    Bash udostępnia dwa sposoby grupowania listy poleceń do wykonania jako jednostkę.

    ( list ) Umieszczenie listy poleceń w nawiasach powoduje utworzenie środowiska podpowłoki i wykonanie każdego polecenia z listy w tej podpowłoce. Ponieważ lista jest wykonywane w podpowłoce, przypisania zmiennych nie działają po zakończeniu podpowłoki.

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

    { list; } Umieszczanie lista poleceń między nawiasami klamrowymi powoduje, że lista jest wykonywana w bieżącym kontekście powłoki . Nie jest tworzona podpowłoka. Wymagany jest średnik (lub znak nowej linii) po liście. Ź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 ie []
    Do porównania ==, !=, <, i > i powinny być używane oraz do porównywania liczbowego eq, ne,lt i gt.

    Rozszerzone nawiasy tj. [[]]

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

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

    • [ jest synonimem polecenia test. Nawet jeśli jest wbudowany w powłokę, tworzy nowy proces.
    • [[ jest jego nową ulepszoną wersją, która jest słowem kluczowym, a nie programem .
    • [[ jest rozumiane przez Korn i Bash.

    Źródło

    Dodaj komentarz

    Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *