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?
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?
Odpowiedzi:
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ż:
[[ $var = $pattern ]]
, jeśli chcesz, co w przeciwnym razie być interpretowane jako wzorzec fnmatch zamiast być interpretowane jako dosłownego łańcucha. Ciąg foo
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ągu foo
), 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
oznacza true
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 (lub test
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łkowita 1+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
lub 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 -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==
.
[[ "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 liczba 0
; porównanie ma wartość Prawda, jeśli drugą liczbą całkowitą jest albo, 0
albo wynikiem ciągu jest 0
.
==
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" ]
==
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
$
[ "$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
.
[[
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.