Automatische Variablenerweiterung innerhalb des Befehls bash [[]]

13

Wenn Sie eine Variable dereferenzieren bash, müssen Sie $sign verwenden. Dennoch scheint es, dass das Folgende gut funktioniert:

x=5
[[ x -gt 2 ]]

Kann das jemand erklären?

Bearbeiten: (mehr Info)

Ich meine, wie und warum der Befehl [[]] meine Variable x ohne das $ -Zeichen dereferenziert. Und ja, wenn x = 1, wird die Anweisung mit false bewertet (Rückgabestatus 1)

Gast
quelle
2
Was meinst du mit "gut arbeiten"? Und ändert sich Ihre Einschätzung, wenn Sie x=1folgen [[ x -gt 2]]?
Nohillside
Ich meine: Wie und warum der Befehl [[]] meine Variable x ohne das $ -Zeichen dereferenziert. Und ja, wenn x = 1, ist die Aussage falsch (Rückgabestatus 1)
Gast

Antworten:

9

Der Grund ist, dass das -eqeine arithmetische Auswertung der Argumente erzwingt.

Ein arithmetischer Operator: -eq, -gt, -lt, -ge, -leund -nein einem [[ ]](in KSH, zsh und bash) Mittel , um automatisch Variablennamen , wie in der C - Sprache zu erweitern, nicht für einen führenden benötigen $.

  • Zur Bestätigung müssen wir uns den Bash-Quellcode ansehen. Das Handbuch bietet keine direkte Bestätigung.

    Innerhalb test.cder Verarbeitung von arithmetischen Operatoren fallen in diese Funktion:

    arithcomp (s, t, op, flags)

    Wo sund tsind beide Operanden. Die Operanden werden an diese Funktion übergeben:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    Die Funktion evalexpist in expr.cder folgenden Kopfzeile definiert:

    /* expr.c -- arithmetic expression evaluation. */

    Also, ja, beide Seiten eines arithmetischen Operators fallen (direkt) in die Auswertung von arithmetischen Ausdrücken. Direkt, kein Aber, kein Wenn.


In der Praxis mit:

 $ x=3

Beides scheitert:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

Was stimmt, xwird nicht erweitert und xist nicht gleich einer Zahl.

Jedoch:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

Die Variable mit dem Namen xwird erweitert (auch ohne $).

Dies passiert nicht für a […]in zsh oder bash (es passiert in ksh).


Das ist dasselbe wie in einem $((…)):

 $ echo $(( x + 7 ))
 10

Und bitte haben Sie Verständnis dafür, dass dies (sehr) rekursiv ist (außer in Bindestrich und Yash):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

Und ziemlich riskant:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

Der Syntaxfehler konnte leicht vermieden werden:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Wie das Sprichwort sagt: Desinfizieren Sie Ihre Eingabe

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

Ende von 😮


Sowohl das (ältere) Externe /usr/bin/test(nicht das eingebaute test) als auch das noch ältere und auch das Externe exprerweitern Ausdrücke nicht nur Ganzzahlen (und anscheinend nur Dezimalzahlen):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument
Isaac
quelle
Interessant. Es ist nicht schwer zu sagen, wie dies möglich ist - als [[Schlüsselwort werden Operatoren und Operanden beim Lesen des Befehls und nicht nach der Erweiterung erkannt. So [[kann behandelt -eqin einer intelligenten Art und Weise als, sagen sie, [. Ich frage mich jedoch, wo wir Dokumentation über die Logik finden, mit der Bash zusammengesetzte Befehle interpretiert? Es sieht für mich nicht ganz offensichtlich aus und ich kann anscheinend keine zufriedenstellenden Erklärungen in manoder finden info bash.
Fra-San
Bash dokumentiert das nirgendwo, wo ich es finden kann. In man ksh93 gibt es eine Art Beschreibung : Die folgenden veralteten arithmetischen Vergleiche sind ebenfalls zulässig: exp1 -eq exp2 . Es ist dieser Text in dem Abschnitt der Mann zshbuiltins arithmetischen Operatoren erwarten Integer - Argumente anstatt arithmetische Ausdrücke . Dies bestätigt, dass einige Argumente vom eingebauten Test unter Bedingungen, die in diesem Zitat nicht angegeben sind, als arithmetische Ausdrücke behandelt werden . Ich werde mit dem Quellcode bestätigen ...test
Isaac
7

Die Operanden der numerischen Vergleiche -eq, -gt, -lt, -ge, -leund -newerden als arithmetischer Ausdrücke genommen. Mit einigen Einschränkungen müssen sie immer noch einzelne Shell-Wörter sein.

Das Verhalten von Variablennamen im arithmetischen Ausdruck wird in Shell Arithmetic beschrieben :

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 zu 0 ausgewertet, wenn auf sie namentlich verwiesen wird, ohne die Parametererweiterungssyntax zu verwenden.

und auch:

Der Wert einer Variablen wird als arithmetischer Ausdruck ausgewertet, wenn auf sie verwiesen wird

Aber ich kann den Teil der Dokumentation nicht finden, in dem es heißt, dass die numerischen Vergleiche arithmetische Ausdrücke annehmen. Es wird weder in Conditional Constructs unter [[noch in Bash Conditional Expressions beschrieben .

Aber experimentell scheint es wie oben gesagt zu funktionieren.

Also, so etwas funktioniert:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

auch dies (der Wert der Variablen wird ausgewertet):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Aber das tut es nicht. Es ist kein einzelnes Shell-Wort, wenn das [[ .. ]]geparst wird. Daher liegt ein Syntaxfehler in der Bedingung vor:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

In anderen arithmetischen Kontexten muss der Ausdruck kein Leerzeichen enthalten. Dies wird ausgegeben 999, da die Klammern den arithmetischen Ausdruck im Index eindeutig abgrenzen:

a[6]=999; echo ${a[1 + 2 + 3]}

Andererseits ist der =Vergleich eine Musterübereinstimmung und beinhaltet weder Arithmetik noch die automatische Variablenerweiterung in einem arithmetischen Kontext (Bedingte Konstrukte):

Wenn die Operatoren ==und !=verwendet werden, wird die Zeichenfolge rechts neben dem Operator als Muster betrachtet und gemäß den im Abschnitt Mustervergleich beschriebenen Regeln abgeglichen, als ob die Option extglob shell aktiviert wäre. Der =Operator ist identisch mit ==.

Das ist also falsch, da die Zeichenfolgen offensichtlich unterschiedlich sind:

[[ "1 + 2 + 3" = 6 ]] 

wie folgt, obwohl die numerischen Werte gleich sind:

[[ 6 = 06 ]] 

und auch hier werden die Saiten ( xund 6) verglichen, sie sind unterschiedlich:

x=6
[[ x = 6 ]]

Dies würde jedoch die Variable erweitern, so dass dies zutrifft:

x=6
[[ $x = 6 ]]
ilkkachu
quelle
Ich kann den Teil der Dokumentation nicht finden, in dem es heißt, dass die numerischen Vergleiche arithmetische Ausdrücke annehmen. Die Bestätigung befindet sich im Code .
Isaac
Das Nächste ist, dass die Beschreibung von arg1 OP arg2besagt, dass die Args positive oder negative Ganzzahlen sein können, was meiner Meinung nach implizieren soll, dass sie als arithmetische Ausdrücke behandelt werden. Verwirrenderweise bedeutet dies auch, dass sie nicht Null sein können. :)
Barmar
@Barmar, ähm, richtig. Das gilt aber auch für die numerischen Vergleiche [, und dort gibt es keine arithmetischen Ausdrücke. Stattdessen beschwert sich Bash über Nicht-Ganzzahlen.
Ilkkachu
@ilkkachu [ist ein externer Befehl, er hat keinen Zugriff auf Shell-Variablen. Es wird oft mit einem eingebauten Befehl optimiert, verhält sich aber trotzdem gleich.
Barmar
@Barmar, ich meinte, dass der Ausdruck "Arg1 und arg2 positive oder negative ganze Zahlen sein können." wird in Bash Conditional Expressions angezeigt , und diese Liste gilt für [genau so gut wie [[. Auch bei sind / müssen [die Operanden to -eqund friends ganze Zahlen sein, so dass auch die Beschreibung gilt. Das Nehmen von "Muss Ganzzahl sein", um "als arithmetische Ausdrücke interpretiert werden" zu bedeuten, gilt in beiden Fällen nicht. (Wahrscheinlich zumindest teilweise, weil Sie sich [wie ein gewöhnlicher Befehl
verhalten
1

Ja, Ihre Beobachtung ist korrekt. Die Variablenerweiterung wird für Ausdrücke in doppelten Klammern durchgeführt [[ ]], sodass Sie keinen $Variablennamen voranstellen müssen.

Dies wird im bashHandbuch explizit angegeben :

[[ Ausdruck ]]

(...) Wortteilung und Pfadnamenerweiterung werden nicht für die Wörter zwischen [[und]] ausgeführt. Tilde-Erweiterung, Parameter- und Variablenerweiterung, arithmetische Erweiterung, Befehlssubstitution, Prozessersetzung und Entfernen von Anführungszeichen werden durchgeführt.

Beachten Sie, dass dies nicht der Fall ist [ ], da [es sich nicht um ein Shell-Schlüsselwort (Syntax) handelt, sondern um einen Befehl (in der Bash ist er eingebaut, andere Shells könnten externe, mit Zeilen versehene Tests verwenden).

jimmij
quelle
1
Vielen Dank für Ihre Antwort. Es scheint, dass dies nur für Zahlen funktioniert. x = city [[$ x == city]] Dies funktioniert nicht ohne das $ -Zeichen.
Gast
3
Es sieht so aus, als gäbe es hier mehr: (x=1; [[ $x = 1 ]]; echo $?)return 0, (x=1; [[ x = 1 ]]; echo $?)return 1, dh die Parametererweiterung wird nicht ausgeführt, xwenn Zeichenfolgen verglichen werden. Dieses Verhalten sieht aus wie eine arithmetische Auswertung, die durch eine arithmetische Erweiterung ausgelöst wird, dh was in passiert (x=1; echo $((x+1))). ( man bashIn Bezug auf die arithmetische Auswertung heißt es: "In einem Ausdruck können Shell-Variablen auch namentlich referenziert werden, ohne die Parametererweiterungssyntax zu verwenden.)
fra-san
@ fra-san Da der -gtOperator eine Zahl erwartet, wird der gesamte Ausdruck neu ausgewertet, als ob er sich im Inneren befindet (()), werden andererseits ==Zeichenfolgen erwartet, sodass stattdessen die Mustervergleichsfunktion ausgelöst wird. Ich habe mich nicht mit dem Quellcode befasst, aber es klingt vernünftig.
Jimmy
[ist eine in bash eingebaute Shell.
Nizam Mohamed
1
@NizamMohamed Es ist ein eingebautes, aber es ist immer noch kein Schlüsselwort.
Kusalananda