Bash-Gleichheitsoperatoren (==, -eq)

134

Kann jemand bitte den Unterschied zwischen -eqund ==in Bash-Skripten erklären ?

Gibt es einen Unterschied zwischen den folgenden?

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

Wird es einfach ==nur verwendet, wenn die Variablen Zahlen enthalten?

Victor Brunell
quelle

Antworten:

185

Es ist umgekehrt: =und ==sind für String-Vergleiche, -eqist für numerische. -eqin der gleichen Familie wie -lt, -le, -gt, -ge, und -ne, wenn das hilft Sie sich daran erinnern , was was ist.

==ist übrigens ein Bash-Ismus. Es ist besser, die POSIX zu verwenden =. In Bash sind die beiden gleichwertig, und in Plain Sh =ist der einzige, der garantiert funktioniert.

$ 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

(Randnotiz: Zitieren Sie diese variablen Erweiterungen! Lassen Sie die obigen doppelten Anführungszeichen nicht aus.)

Wenn Sie eine gerade schreiben #!/bin/bashSkript dann empfehle ich mit [[statt . Die doppelte Form bietet mehr Funktionen, eine natürlichere Syntax und weniger Fallstricke, die Sie stören werden. $aZum einen sind keine doppelten Anführungszeichen mehr erforderlich :

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

Siehe auch:

John Kugelman
quelle
1
@DJCrashdummy, [[als Ganzes ist ein Ksh -Ismus aus den 1980er Jahren, den Bash (und viele andere Shells) angenommen haben. Das ist der springende Punkt - wenn Sie [[ überhaupt , dann kann man mit Sicherheit davon ausgehen , dass alle Erweiterungen um ihn herum (implementiert KSH fnmatch()-Stil Pattern - Matching, ERE reguläre Ausdrücke mit =~, und ja, Unterdrückung des String-Splitting und Dateisystem Globbing) werden verfügbar. Da [[es sich in seiner Gesamtheit um eine Nicht-POSIX-Syntax handelt, gibt es keinen zusätzlichen Portabilitätsverlust bei der Annahme, dass die Funktionen, mit denen es geboren wurde, verfügbar sind.
Charles Duffy
1
@DJCrashdummy, ... der Link, den Sie anzeigen, weist korrekt darauf hin, dass manchmal Anführungszeichen auf der rechten Seite von benötigt werden [[ $var = $pattern ]], wenn Sie möchten, dass das, was sonst als fnmatch-Muster interpretiert würde, stattdessen als Literalzeichenfolge interpretiert wird. Die Zeichenfolge fooist nicht wörtlich interpretiert, sodass Anführungszeichen völlig sicher sind. Nur wenn das OP beispielsweise übereinstimmen möchte foo*(mit dem Sternchen als Literal, was nichts bedeutet, was nach der Zeichenfolge kommen kann foo), sind Anführungszeichen oder Escapezeichen erforderlich.
Charles Duffy
@ CharlesDuffy Leider habe ich keine Ahnung, worauf Sie sich beziehen, da mein Kommentar gelöscht zu sein scheint und ich mich nicht erinnern kann, weil es eine ziemliche Zeit war. :-(
DJCrashdummy
@DJCrashdummy, ... huh, ich nahm an, du hättest es selbst zurückgezogen. Im Grunde war es ein Einwand gegen nicht [[zitierte Erweiterungen innerhalb eines Bashismus (was darauf hinweist, dass dies der einzige Grund war, warum Sie der Antwort keine +1 gaben).
Charles Duffy
@CharlesDuffy nein, das ist nicht der einzige Grund: Abgesehen von der Tatsache, dass ich Bash-Ismen, Ksh-Ismen usw. für einfache Aufgaben nicht mag (es verursacht nur Probleme und verwirrt Anfänger), verstehe ich nicht, warum ich Single mische Klammern [ ... ]und doppeltes Gleichheitszeichen ==. : - /
DJCrashdummy
27

Dies hängt vom Testkonstrukt um den Bediener ab. Ihre Optionen sind doppelte Klammern, doppelte Klammern, einfache Klammern oder Test

Wenn Sie ((...)) verwenden , testen Sie das arithmetische Eigenkapital mit ==wie in C:

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

(Hinweis: 0bedeutet trueim Unix-Sinne und ungleich Null ist ein fehlgeschlagener Test)

Die Verwendung -eqinnerhalb doppelter Klammern ist ein Syntaxfehler.

Wenn Sie [...] (oder eine einfache Klammer) oder [...]] (oder eine doppelte Klammer) verwenden oder testeine von -eq, -ne, -lt, -le, -gt oder verwenden -ge als arithmetischer Vergleich .

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

Das ==Innere von einfachen oder doppelten Klammern (oder testBefehlen) ist einer der Zeichenfolgenvergleichsoperatoren :

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

=Entspricht als Zeichenfolgenoperator ==dem Leerzeichen =oder ==dem erforderlichen Leerzeichen und notiert es.

Während Sie dies tun können [[ 1 == 1 ]]oder [[ $(( 1+1 )) == 2 ]]es die Zeichenfolgengleichheit testet - nicht die arithmetische Gleichheit.

So -eqergibt sich das wahrscheinlich erwartete Ergebnis , dass der ganzzahlige Wert von 1+1gleich ist, 2obwohl die relative Luftfeuchtigkeit eine Zeichenfolge ist und ein nachfolgendes Leerzeichen hat:

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

Während ein String-Vergleich desselben den nachgestellten Platz einnimmt und der String-Vergleich daher fehlschlägt:

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

Und ein falscher Zeichenfolgenvergleich kann zu einer völlig falschen Antwort führen. '10' ist lexikographisch kleiner als '2', daher gibt ein Zeichenfolgenvergleich trueoder zurück 0. So viele sind von diesem Fehler gebissen:

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

gegen den korrekten Test für 10, der arithmetisch kleiner als 2 ist:

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

In Kommentaren gibt es eine Frage des technischen Grundes, warum die Verwendung der Ganzzahl -eqfür Zeichenfolgen True für Zeichenfolgen zurückgibt, die nicht identisch sind:

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

Der Grund ist, dass Bash untypisiert ist . Dies -eqbewirkt, dass die Zeichenfolgen nach Möglichkeit als Ganzzahlen interpretiert werden, einschließlich Basiskonvertierung:

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

Und 0wenn Bash denkt, dass es nur eine Zeichenfolge ist:

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

Ist [[ "yes" -eq "no" ]]also gleichbedeutend mit[[ 0 -eq 0 ]]

Letzter Hinweis: Viele der Bash-spezifischen Erweiterungen der Testkonstrukte sind nicht POSIX und schlagen daher in anderen Shells fehl. Andere Schalen unterstützen [[...]]und ((...))oder im Allgemeinen nicht== .

dawg
quelle
Ich bin gespannt auf den technischen Grund für die [[ "yes" -eq "no" ]]Rückgabe von True. Wie zwingt bash diese Zeichenfolgen zu ganzzahligen Werten, die verglichen werden können? ;-)
Odony
4
Bash Variablen sind nicht typisiert so [[ "yes" -eq "no" ]]entspricht [[ "yes" -eq 0 ]] oder [[ "yes" -eq "any_noninteger_string" ]]- All True. Der -eqGanzzahlvergleich der Kräfte. Das "yes"wird als ganze Zahl interpretiert 0; Der Vergleich ist True, wenn die andere Ganzzahl entweder 0oder das Ergebnis der Zeichenfolge ist 0.
Morgengrauen
Boo, hiss re: zeigt (nicht portierbar) ==in den Codebeispielen und erwähnt nur (portabel, standardisiert) =darunter.
Charles Duffy
24

==ist ein bash-spezifischer Alias ​​für =und führt anstelle eines numerischen Vergleichs einen (lexikalischen) Zeichenfolgenvergleich durch. eqnatürlich ein numerischer Vergleich.

Schließlich bevorzuge ich normalerweise das Formular if [ "$a" == "$b" ]

Elliott Frisch
quelle
15
Die Verwendung ==hier ist eine schlechte Form, wie nur =von POSIX angegeben.
Charles Duffy
9
Wenn Sie wirklich darauf bestehen, ==setzen Sie es zwischen [[und ]]. (Und stellen Sie sicher, dass die erste Zeile Ihres Skripts die Verwendung angibt /bin/bash.)
Holgero
18

Jungs: Mehrere Antworten zeigen gefährliche Beispiele. In dem Beispiel von OP wurde [ $a == $b ]speziell die Substitution nicht zitierter Variablen verwendet (Stand: Bearbeitung vom 17. Oktober). Denn [...]das ist sicher für die String-Gleichheit.

Wenn Sie jedoch Alternativen wie aufzählen möchten [[...]], müssen Sie auch darüber informieren, dass die rechte Seite zitiert werden muss. Wenn nicht zitiert, ist es eine Musterübereinstimmung! (Aus der Bash-Manpage: "Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um zu erzwingen, dass es als Zeichenfolge abgeglichen wird.")

Hier in Bash sind die beiden Anweisungen, die "Ja" ergeben, Mustervergleich, die anderen drei sind Zeichenfolgengleichheit:

$ 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
$
bk-se
quelle
2
Muss [ "$lht" = "$rht" ] mit den Zitaten sein , um auch für Gleichheit zuverlässig zu sein. Wenn Sie eine Datei mit erstellt haben touch 'Afoo -o AB', [ $lft = $rht ]wird true zurückgegeben, obwohl dieser Dateiname überhaupt nicht mit identisch ist AB.
Charles Duffy