Czy ktoś mógłby wyjaśnić różnicę pomiędzy =
, ==
oraz -eq
w 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?
Jest na odwrót: =
i ==
dotyczy porównań ciągów, -eq
dotyczy liczb. -eq
jest 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/bash
scenariusz 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ż:
[[
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.[[ $var = $pattern ]]
, jeśli chcesz, co w przeciwnym razie być interpretowane jako wzorzec fnmatch zamiast być interpretowane jako dosłownego łańcucha. Ciągfoo
nie 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ągufoo
), potrzebne byłyby cudzysłowy lub znaki specjalne.[[
niecytowanych rozszerzeń wewnątrz bycia bashizmem (wskazujący, że był to jedyny powód, dla którego nie dałeś odpowiedzi +1).[ ... ]
i podwójny znak równości==
. : - /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:
0
oznaczatrue
w sensie uniksowym, a wartość różna od zera oznacza niepowodzenie testu)Użycie
-eq
wewnątrz podwójnego nawiasu jest błędem składniowym.Jeśli używasz [...] (lub pojedynczego nawiasu klamrowego) lub [[...]] (lub podwójnego nawiasu klamrowego), lub
test
moż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 (lubtest
poleceń) 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
-eq
daje wynik prawdopodobnie oczekiwany, że wartość całkowita1+1
jest równa,2
mimo ż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
true
lub0
. 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
-eq
w ł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 .
-eq
Powoduje 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
0
jeś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==
.źródło
[[ "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ć? ;-)[[ "yes" -eq "no" ]]
jest równoważne[[ "yes" -eq 0 ]]
lub[[ "yes" -eq "any_noninteger_string" ]]
- All True. Te-eq
siły całkowitych porównanie."yes"
Jest interpretowane jako liczba0
; porównanie ma wartość Prawda, jeśli drugą liczbą całkowitą jest albo,0
albo wynikiem ciągu jest0
.==
w próbkach kodu i tylko wzmianka (przenośna, standaryzowana)=
poniżej.==
jest aliasem specyficznym dla basha dla=
i wykonuje porównanie łańcuchowe (leksykalne) zamiast porównania numerycznego.eq
będąc oczywiście porównaniem numerycznym.Wreszcie, zazwyczaj wolę korzystać z formularza
if [ "$a" == "$b" ]
źródło
==
tutaj jest złą formą, ponieważ=
określa tylko POSIX.==
umieść go między[[
a]]
. (I upewnij się, że pierwsza linia twojego skryptu wskazuje na użycie/bin/bash
.)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 $
źródło
[ "$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 zAB
.