Gibt es einen wesentlichen Unterschied beim Vergleichen einer Variablen als Zeichenfolge oder als Int

22

Aus Neugier ist es bei einem Bash-Variablen-Vergleich (dessen Wert ist ein integer) möglich, ihn mit einem vordefinierten Wert zu testen, der entweder als intoder als deklariert ist string.

Beispielskript :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Ausgabe :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

und

$  ./param.sh blah
$

Beide Funktionen verhalten sich gleich, und ich frage mich, ob es beim Überprüfen einer Ganzzahlvariablen einen bevorzugten Weg gibt. Ich würde für die Überprüfung gehen im intVergleich zu, intwie es ist strenger , aber ich frage mich , ob es irgendwelche ziehen Rücken tun es mit string?

In diesem Fall geht f2()es auch strenger um den Vergleich, dh das Übergeben eines Dezimalwertes unterbricht diesen, wohingegen f1()es kein Problem darstellt.

fduff
quelle
Beachten Sie, dass Bash keinen Integer-Datentyp hat. Sie können Bash einfach anweisen, einen String als Ganzzahl zu behandeln.
Hilfemethode

Antworten:

18

Ja, viele Unterschiede. Zum Beispiel =prüft , ob exakte Zeichenfolge Gleichheit, sondern -eqwertet arithmetisch beide Ausdrücke , bevor für die Gleichstellung Überprüfung:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Außerdem ist die leere Zeichenfolge numerisch gleich Null:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

Und eine ganze andere Klasse von Unterschieden tritt auf, wenn Sie die Vergleichsoperatoren einbeziehen - unter Berücksichtigung von <vs -lt, zum Beispiel:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Dies liegt daran, dass die Zeichenfolge "2" alphabetisch nach der Zeichenfolge "10" steht (da 1 vor 2 steht), die Zahl "2" jedoch numerisch kleiner als die Zahl "10" ist.

godlygeek
quelle
2
Vergessen Sie nicht, dass es auch (( ... ))numerische Operationen gibt. (( " 1 " == 1 )) && echo yes || echo noErgebnisse inyes
Patrick
7

Der Vergleich zwischen Ganzzahl und Zeichenfolge wird am wichtigsten, wenn Sie einen Vergleich durchführen, der größer oder kleiner ist als:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

Das erste scheitert, weil 9 nach 11 kommt, wenn es lexikographisch sortiert ist.

Beachten Sie, dass die Verwendung von Anführungszeichen nicht bestimmt, ob Sie Zeichenfolgen oder Zahlen vergleichen, sondern der Operator. Sie können die obigen Anführungszeichen hinzufügen oder entfernen, es macht keinen Unterschied. Bash fängt undefinierte Variablen in doppelten Klammern ab, sodass keine Anführungszeichen erforderlich sind. Die Verwendung von Anführungszeichen mit einzelnen Klammern für numerische Tests wird Sie nicht retten, da:

[ "" -lt 11 ]

ist sowieso ein Fehler ("Integer-Ausdruck erforderlich"). Anführungszeichen sind ein wirksamer Schutz bei Zeichenfolgenvergleichen in einzelnen Klammern:

[ "" \< 11 ]

Hinweis innerhalb doppelten Klammern, ""werden -eq 0aber nicht == 0.

Goldlöckchen
quelle
1
In bash ist es nicht zwingend erforderlich, Variablen in doppelten Klammern anzugeben: Die integrierte Funktion [[merkt sich, wo sich die Variablen befinden, und lässt sich nicht von leeren Variablen täuschen. Einzelne Klammern ( [) haben diese Funktion nicht und erfordern Anführungszeichen.
Glenn Jackman
@glennjackman Hatte das nicht bemerkt. [[ -lt 11 ]]ist ein Fehler, ist es aber nothing=; [[ $nothing -lt 11 ]]nicht. Ich habe den letzten Absatz ein wenig überarbeitet.
Goldlöckchen
2

Zusätzlich zu dem, was gesagt wurde.
Das Vergleichen auf Gleichheit ist mit Zahlen schneller, obwohl Sie in Shell-Skripten selten eine schnelle Berechnung benötigen.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s
Emmanuel
quelle
In Anbetracht der Tatsache, dass sie verschiedene Dinge tun, ist die Leistung irrelevant - Sie müssen diejenige verwenden, die das tut, was Sie wollen.
Godlygeek
@godlygeek Gleichheitsvergleich einer Variablen kann in beide Richtungen erreicht werden. "-eq" ist schneller.
Emmanuel
Sie prüfen auf unterschiedliche Definitionen von Gleichheit. Wenn Sie die Frage "Enthält diese Variable die exakte Zeichenfolge 123" beantworten möchten, können Sie nur diese verwenden =, da die Verwendung -eqauch für "+123" passen würde. Wenn Sie wissen möchten, dass "Ist diese Variable, wenn sie als arithmetischer Ausdruck ausgewertet wird, gleich 123", können Sie nur verwenden -eq. Das einzige Mal, dass ich sehen kann, wo es einem Programmierer egal ist, welche Definition von Gleichheit verwendet wurde, ist, wenn er weiß, dass der Inhalt der Variablen im Voraus auf ein bestimmtes Muster beschränkt ist.
Godlygeek
@godlygeek Interessanterweise ging es bei der Frage darum, die Gleichheit von Zahlen so zu vergleichen, als wären sie Zeichenfolgen. Passt sie zum Fall von Variablen, die im Voraus auf ein bestimmtes Muster beschränkt wurden?
Emmanuel
Ihr Beispiel ( b=234) passt zu diesem Muster - Sie wissen, dass es nicht +234 oder "234" oder "233 + 1" ist, da Sie es selbst zugewiesen haben, sodass Sie wissen, dass der Vergleich als Zeichenfolge und als Zahl gleichermaßen gültig ist. Aber das Skript des OP, da es Eingang als Befehlszeilenargument nimmt, nicht über diese Einschränkung - zu prüfen , nennt es als ./param.sh 0+1oder./param.sh " 1"
godlygeek