Operatory równości powłoki (=, ==, -eq)


148

Czy ktoś mógłby wyjaśnić różnicę pomiędzy =, ==oraz -eqw skryptów powłoki?

Czy jest jakaś różnica między poniższymi?

[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]

Czy to po prostu to =i ==są używane tylko wtedy, gdy zmienne zawierają liczby?

Odpowiedzi:


205

Jest na odwrót: =i ==dotyczy porównań ciągów, -eqdotyczy liczb. -eqjest w tej samej rodziny jako -lt, -le, -gt, -ge, i -ne, jeśli to pomaga zapamiętać, który jest który.

==Nawiasem mówiąc, jest bashizmem. Lepiej jest użyć POSIX =. W bashu te dwa są równoważne, aw zwykłym sh =jest jedynym gwarantowanym działaniem.

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(Uwaga dodatkowa: zacytuj te rozszerzenia zmiennych! Nie pomijaj powyższych podwójnych cudzysłowów).

Jeśli piszesz #!/bin/bashscenariusz to polecam używając [[zamiast . Podwojona forma ma więcej funkcji, bardziej naturalną składnię i mniej problemów, które mogą Cię zaskoczyć. Podwójne cudzysłowy nie są już wymagane $a, na przykład:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

Zobacz też:


2
@DJCrashdummy, [[jako całość to ksh-izm z lat 80., który został przyjęty przez bash (i wiele innych powłok). O to właśnie chodzi - jeśli [[ w ogóle masz, możesz bezpiecznie założyć, że wszystkie rozszerzenia ksh zaimplementowane wokół niego ( fnmatch()dopasowywanie wzorców -styl, wyrażenia regularne ERE z =~i tak, tłumienie dzielenia łańcuchów i globowania systemu plików) będą dostępny. Ponieważ [[jest to składnia inna niż POSIX w całości, nie ma dodatkowej utraty przenośności przy założeniu, że funkcje, z którymi się urodził, będą dostępne.
Charles Duffy

1
@DJCrashdummy ... link pokażesz punktów poprawnie że cytaty są czasami potrzebne na prawej stronie z [[ $var = $pattern ]], jeśli chcesz, co w przeciwnym razie być interpretowane jako wzorzec fnmatch zamiast być interpretowane jako dosłownego łańcucha. Ciąg foonie ma interpretacji nieliteralnej, dzięki czemu pozostawienie cudzysłowów jest całkowicie bezpieczne; tylko wtedy, gdy OP chciałby dopasować, powiedzmy, foo*(z gwiazdką jako dosłowną, nie oznaczającą niczego, co może wystąpić po ciągu foo), potrzebne byłyby cudzysłowy lub znaki specjalne.
Charles Duffy

@CharlesDuffy niestety nie mam pojęcia, o czym mówisz, ponieważ mój komentarz wydaje się być usunięty i nie mogę sobie przypomnieć, ponieważ minęło sporo czasu. :-(
DJCrashdummy

@DJCrashdummy, ... huh, zakładałem, że sam to wycofałeś. Zasadniczo był to zarzut dotyczący [[niecytowanych rozszerzeń wewnątrz bycia bashizmem (wskazujący, że był to jedyny powód, dla którego nie dałeś odpowiedzi +1).
Charles Duffy

@CharlesDuffy nie, to nie jedyny powód: poza tym, że nie lubię bash-isms, ksh-isms itp. Do prostych zadań (to tylko powoduje kłopoty i dezorientuje początkujących), nie rozumiem, dlaczego miksowanie singli szelki [ ... ]i podwójny znak równości ==. : - /
DJCrashdummy

29

Zależy to od Konstrukcji Testowej wokół operatora. Dostępne opcje to podwójne nawiasy, podwójne nawiasy klamrowe, pojedyncze nawiasy klamrowe lub test

Jeśli używasz ((...)) , testujesz arytmetyczną wartość równą ==jak w C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(Uwaga: 0oznacza truew sensie uniksowym, a wartość różna od zera oznacza niepowodzenie testu)

Użycie -eqwewnątrz podwójnego nawiasu jest błędem składniowym.

Jeśli używasz [...] (lub pojedynczego nawiasu klamrowego) lub [[...]] (lub podwójnego nawiasu klamrowego), lub testmożesz użyć jednego z -eq, -ne, -lt, -le, -gt lub -ge jako porównanie arytmetyczne .

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

==Wewnątrz pojedynczymi lub podwójnymi szelkami (lub testpoleceń) jest jednym z operatorów porównania ciąg :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

Jako operator łańcuchowy =jest równoważny ==i zwraca uwagę na białe znaki wokół =lub ==wymagane.

Chociaż możesz to zrobić [[ 1 == 1 ]]lub [[ $(( 1+1 )) == 2 ]]testuje równość ciągów - nie równość arytmetyczną.

Więc -eqdaje wynik prawdopodobnie oczekiwany, że wartość całkowita 1+1jest równa, 2mimo że RH jest łańcuchem i ma spację na końcu:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

Podczas gdy ciągowe porównanie tego samego zbiera końcową spację i dlatego porównanie ciągów kończy się niepowodzeniem:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

A błędne porównanie ciągów może dać kompletną złą odpowiedź. „10” jest leksykograficznie mniejsze niż „2”, więc porównanie ciągów zwraca truelub 0. Tak wielu gryzie ten błąd:

$ [[ 10 < 2 ]]; echo $?
0

vs poprawny test dla 10 jest arytmetycznie mniejszych niż 2:

$ [[ 10 -lt 2 ]]; echo $?
1

W komentarzach pojawia się kwestia technicznego powodu używania liczby całkowitej -eqw łańcuchach zwraca True dla łańcuchów, które nie są takie same:

$ [[ "yes" -eq "no" ]]; echo $?
0

Powodem jest to, że Bash nie ma typu . -eqPowoduje struny należy interpretować jako liczby całkowite , jeśli to możliwe , w tym konwersji bazy:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

A 0jeśli Bash myśli, że to tylko ciąg:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

Więc [[ "yes" -eq "no" ]]jest równoważne[[ 0 -eq 0 ]]

Ostatnia uwaga: Wiele rozszerzeń Konstrukcji Testowych specyficznych dla Bash nie jest POSIX-owych i dlatego nie powiedzie się w innych powłokach. Inne powłoki generalnie nie obsługują [[...]]i ((...))lub== .


Jestem ciekaw powodu technicznej dla [[ "yes" -eq "no" ]]powrocie prawda. W jaki sposób bash przekształca te ciągi w wartości całkowite, które można porównać? ;-)
odony

4
Zmienne Bash nie mają typu, więc [[ "yes" -eq "no" ]]jest równoważne [[ "yes" -eq 0 ]] lub [[ "yes" -eq "any_noninteger_string" ]]- All True. Te -eqsiły całkowitych porównanie. "yes"Jest interpretowane jako liczba 0; porównanie ma wartość Prawda, jeśli drugą liczbą całkowitą jest albo, 0albo wynikiem ciągu jest 0.
dawg

Boo, syk re: pokazanie (nieprzenoszalne) ==w próbkach kodu i tylko wzmianka (przenośna, standaryzowana) =poniżej.
Charles Duffy,

24

==jest aliasem specyficznym dla basha dla =i wykonuje porównanie łańcuchowe (leksykalne) zamiast porównania numerycznego. eqbędąc oczywiście porównaniem numerycznym.

Wreszcie, zazwyczaj wolę korzystać z formularza if [ "$a" == "$b" ]


15
Użycie ==tutaj jest złą formą, ponieważ =określa tylko POSIX.
Charles Duffy

9
Jeśli naprawdę nalegasz na użycie, ==umieść go między [[a ]]. (I upewnij się, że pierwsza linia twojego skryptu wskazuje na użycie /bin/bash.)
holgero

18

Chłopaki: Kilka odpowiedzi zawiera niebezpieczne przykłady. Przykład OP [ $a == $b ]wykorzystywał konkretnie niecytowane podstawianie zmiennych (stan na październik 2017). Bo [...]to jest bezpieczne dla równości łańcuchów.

Ale jeśli masz zamiar wyliczyć alternatywy, takie jak [[...]], musisz również poinformować, że należy zacytować prawą stronę. Jeśli nie jest cytowany, jest to dopasowanie do wzorca! (Ze strony podręcznika bash: "Dowolna część wzorca może być cytowana w cudzysłowie, aby wymusić dopasowanie jako łańcuch.").

Tutaj, w bash, dwie instrukcje dające „tak” są dopasowywaniem do wzorca, pozostałe trzy to równość ciągów:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$

2
Musi być [ "$lht" = "$rht" ] z cudzysłowami, aby były wiarygodne nawet dla równości. Jeśli masz plik utworzony za pomocą touch 'Afoo -o AB', [ $lft = $rht ]zwróci wartość true, nawet jeśli nazwa pliku nie jest identyczna z AB.
Charles Duffy,
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.