Zeichenfolgenvergleich mit Ganzzahl in [[Test

7

Ich habe mir hier eine Diskussion zwischen Kusalananda und xhienne angesehen , in der erwähnt [ "" -ge 2 ]wird , dass es sich nicht um einen gültigen Test handelt, der einen Fehler in bash --posixund anderen POSIX-kompatiblen Shells erzeugt.

bash-4.3$ [ "" -gt 10 ]
bash: [: : integer expression expected
bash-4.3$ [ '' -gt 10 ]
bash: [: : integer expression expected

Alles gut dort. Aus Neugier versuchte ich dasselbe mit [[.

bash-4.3$ [[ "" -gt 10 ]] && echo "YES"
bash-4.3$ [[ "" -gt 0 ]] && echo "YES"
bash-4.3$ [[ "" -gt -1 ]] && echo "YES"
YES
bash-4.3$ [[ "" -eq 0 ]] && echo "YES"
YES

Wie Sie sehen können, gibt es keine Fehler und es wird tatsächlich als numerischer Ausdruck ausgewertet, wobei "" gleich 0 ist. Was passiert also genau hier? Ist es [[einfach inkonsistent mit dem alten testoder POSIX? Führt es einfach einen Zeichenfolgenvergleich statt einen numerischen Vergleich durch?

Sergiy Kolodyazhnyy
quelle

Antworten:

14

Ein Unterschied zwischen [und [[ist , dass [sie nicht arithmetische Auswertung tun , sondern [[tut:

$ [ "2 + 2" -eq 4 ] && echo yes
bash: [: 2 + 2: integer expression expected
$ [[ "2 + 2" -eq 4 ]] && echo yes
yes

Die zweite Feinheit ist, dass leere Zeichenfolgen überall dort, wo eine arithmetische Auswertung unter bash durchgeführt wird, mit 0 ausgewertet werden. Zum Beispiel:

$ x=""; echo $((0 + x))
0
$ [[ "" -eq 0 ]] && echo yes
yes

Dokumentation

Von man bash:

Shell-Variablen sind als Operanden zulässig. Die Parametererweiterung wird durchgeführt, bevor der Ausdruck ausgewertet wird. Innerhalb eines Ausdrucks können Shell-Variablen auch nach Namen referenziert werden, ohne die Parametererweiterungssyntax zu verwenden. Eine Shell-Variable, die null oder nicht gesetzt ist, wird bei Verwendung des Namens ohne Verwendung der Parametererweiterungssyntax mit 0 ausgewertet. Der Wert einer Variablen wird als arithmetischer Ausdruck ausgewertet, wenn auf ihn verwiesen wird oder wenn einer Variablen, der das ganzzahlige Attribut mit declare -i zugewiesen wurde, ein Wert zugewiesen wird. Ein Nullwert ergibt 0 . Für eine Shell-Variable muss das Integer-Attribut nicht aktiviert sein, damit es in einem Ausdruck verwendet werden kann. [ Hervorhebung hinzugefügt]

Nebenbei: Sicherheitsprobleme

Beachten Sie, dass die arithmetische Auswertung von bash ein potenzielles Sicherheitsproblem darstellt. Betrachten Sie zum Beispiel:

x='a[$(rm -i *)]'
[[ x -eq 0 ]] && echo yes

Mit der -iOption ist das oben Genannte sicher, aber die allgemeine Lektion besteht darin, die arithmetische Auswertung von bash nicht mit nicht bereinigten Daten zu verwenden.

Im Gegensatz dazu wird [keine arithmetische Auswertung durchgeführt, und folglich versucht der Befehl niemals, Dateien zu löschen. Stattdessen wird sicher ein Fehler generiert:

$ x='a[$(rm -i *)]'
$ [ "$x" -eq 0 ] && echo yes
bash: [: a[$(rm -i *)]: integer expression expected

Weitere Informationen zu diesem Thema finden Sie in dieser Antwort .

John1024
quelle
Aha. [[arithmetische Auswertung, das hat gefehlt. Ich denke, Sie sollten das als obersten Punkt setzen
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Sehr gut. Ich habe die beiden Punkte in der Antwort neu angeordnet.
John1024
3

Ja, posix test ( [) konvertiert bei numerischen Vergleichen keine Zeichenfolge in eine Zahl:

$ sh -c '[ 2+2 -eq 4 ]'
sh: 1: [: Illegal number: 2+2

$  dash -c '[ 2+2 -eq 4 ]'
dash: 1: [: Illegal number: 2+2

$ bash -c '[ 2+2 -eq 4 ] && echo "YES"'
bash: line 0: [: 2+2: integer expression expected

Es funktionieren jedoch nicht alle Schalen auf die gleiche Weise:

$ ksh -c '[ 2+2 -eq 4 ] && echo "YES"'
YES

Übliche Problemumgehung

Stellen Sie sicher, dass ein Null- oder Leerwert in 0 konvertiert wird (funktioniert bei den meisten Shells).

$ dash -c 'a=""; [ "${a:-0}" -gt 3 ] && echo "YES"'

Verwenden Sie Arithmetik

Verwenden Sie eine arithmetische Erweiterung (kann auch Werte wie 2+2in einigen Shells konvertieren (nicht Bindestrich))

$ dash -c 'a=""; [ "$((a+0))" -gt -3 ] && echo "YES"'
YES

Verwenden [[

Die Verwendung des [[Tests konvertiert die meisten Zeichenfolgen, die zu einer Zahl werden (auch wenn dies nicht gewünscht wird), in Shells, die Folgendes ermöglichen [[:

$ [[ "2+2" -gt 3 ]] && echo "YES"
YES
Isaac
quelle
Ich habe mich mehr mit dem "Warum" eines solchen Verhaltens befasst, in [[dem Ihre Antwort nicht zu berühren scheint, aber immer noch relevante Informationen. Ich bin neugierig , warum kshGeräte [auf diese Weise.
Sergiy Kolodyazhnyy
Nun, es tut : convert most strings that would become a number. Das ist der Grund. Was den philosophischen Grund des Programmierers betrifft, der ihn so implementiert hat, sollten Sie ihn fragen. @SergiyKolodyazhnyy
Isaac
lol, vertrau mir, wenn ich David Korns E-Mail hätte, würde ich XD
Sergiy Kolodyazhnyy
Dies kann noch gültig sein ;-) oder Github suchen. @SergiyKolodyazhnyy
Isaac