Update: Einige Leute sagen, man sollte niemals eval verwenden. Ich bin nicht einverstanden. Ich denke, das Risiko entsteht, wenn korrupte Eingaben an weitergegeben werden können eval
. Es gibt jedoch viele häufige Situationen, in denen dies kein Risiko darstellt, und daher lohnt es sich zu wissen, wie man eval auf jeden Fall verwendet. Diese Stackoverflow-Antwort erklärt die Risiken der Bewertung und Alternativen zur Bewertung. Letztendlich ist es Sache des Benutzers, zu bestimmen, ob / wann die Bewertung sicher und effizient ist.
Mit der Bash- eval
Anweisung können Sie Codezeilen ausführen, die von Ihrem Bash-Skript berechnet oder erfasst wurden.
Das vielleicht einfachste Beispiel wäre ein Bash-Programm, das ein anderes Bash-Skript als Textdatei öffnet, jede Textzeile liest und verwendet eval
, um sie der Reihe nach auszuführen. Dies ist im Wesentlichen das gleiche Verhalten wie bei der Bash- source
Anweisung, die verwendet wird, es sei denn, es ist erforderlich, eine Art Transformation (z. B. Filtern oder Ersetzen) für den Inhalt des importierten Skripts durchzuführen.
Ich habe es selten gebraucht eval
, aber ich fand es nützlich, Variablen zu lesen oder zu schreiben, deren Namen in Zeichenfolgen enthalten waren, die anderen Variablen zugewiesen wurden. Zum Beispiel, um Aktionen für Variablensätze auszuführen, während der Code-Footprint klein gehalten und Redundanz vermieden wird.
eval
ist konzeptionell einfach. Die strenge Syntax der Bash-Sprache und die Parsing-Reihenfolge des Bash-Interpreters können jedoch nuanciert werden und eval
kryptisch und schwer zu verwenden oder zu verstehen erscheinen. Hier sind die wesentlichen Punkte:
Das übergebene Argument eval
ist ein Zeichenfolgenausdruck , der zur Laufzeit berechnet wird. eval
führt das endgültige analysierte Ergebnis seines Arguments als tatsächliche Codezeile in Ihrem Skript aus.
Syntax und Analysereihenfolge sind streng. Wenn das Ergebnis keine ausführbare Zeile mit Bash-Code ist, stürzt das Programm im Rahmen Ihres Skripts bei der eval
Anweisung ab, wenn es versucht, Müll auszuführen.
Beim Testen können Sie die eval
Anweisung durch ersetzen echo
und sehen, was angezeigt wird. Wenn es sich im aktuellen Kontext um legitimen Code handelt, eval
funktioniert das Durchlaufen .
Die folgenden Beispiele können helfen, die Funktionsweise von eval zu verdeutlichen ...
Beispiel 1:
eval
Anweisung vor 'normalem' Code ist ein NOP
$ eval a=b
$ eval echo $a
b
Im obigen Beispiel haben die ersten eval
Aussagen keinen Zweck und können beseitigt werden. eval
ist in der ersten Zeile sinnlos, da der Code keinen dynamischen Aspekt aufweist, dh er wurde bereits in die letzten Zeilen des Bash-Codes analysiert, sodass er mit einer normalen Code-Anweisung im Bash-Skript identisch wäre. Der zweite eval
ist ebenfalls sinnlos, da es zwar einen Parsing-Schritt gibt, der $a
in sein Literal-String-Äquivalent konvertiert , aber keine Indirektion (z. B. keine Referenzierung über den String-Wert eines tatsächlichen Bash-Substantivs oder einer Bash-gehaltenen Skriptvariablen) erfolgt, sodass er sich identisch verhält als Codezeile ohne eval
Präfix.
Beispiel 2:
Führen Sie die Variablenzuweisung mit Variablennamen durch, die als Zeichenfolgenwerte übergeben werden.
$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval
Wenn Sie echo $key=$val
wären, wäre die Ausgabe:
mykey=myval
Das , das endgültige Ergebnis der String - Parsing ist, ist das, was von eval ausgeführt werden, damit das Ergebnis der Echo - Anweisung am Ende ...
Beispiel 3:
Hinzufügen von mehr Indirektion zu Beispiel 2
$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing
Das Obige ist etwas komplizierter als das vorherige Beispiel und stützt sich stärker auf die Parsing-Reihenfolge und die Besonderheiten von Bash. Die eval
Zeile wird intern grob in der folgenden Reihenfolge analysiert (beachten Sie, dass die folgenden Anweisungen Pseudocode und kein echter Code sind, nur um zu zeigen, wie die Anweisung intern in Schritte unterteilt wird, um zum Endergebnis zu gelangen) .
eval eval \$$keyA=\$$valA # substitution of $keyA and $valA by interpreter
eval eval \$keyB=\$valB # convert '$' + name-strings to real vars by eval
eval $keyB=$valB # substitution of $keyB and $valB by interpreter
eval that=amazing # execute string literal 'that=amazing' by eval
Wenn die angenommene Analysereihenfolge nicht erklärt, was eval genug tut, beschreibt das dritte Beispiel das Parsen möglicherweise detaillierter, um zu klären, was vor sich geht.
Beispiel 4:
Ermitteln Sie, ob Vars, deren Namen in Zeichenfolgen enthalten sind, selbst Zeichenfolgenwerte enthalten.
a="User-provided"
b="Another user-provided optional value"
c=""
myvarname_a="a"
myvarname_b="b"
myvarname_c="c"
for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
eval varval=\$$varname
if [ -z "$varval" ]; then
read -p "$varname? " $varname
fi
done
In der ersten Iteration:
varname="myvarname_a"
Bash analysiert das Argument eval
und eval
sieht dies zur Laufzeit buchstäblich:
eval varval=\$$myvarname_a
Der folgende Pseudocode versucht zu veranschaulichen, wie bash die obige Zeile des realen Codes interpretiert , um zu dem endgültigen Wert zu gelangen, der von ausgeführt wird eval
. (Die folgenden Zeilen beschreiben den nicht genauen Bash-Code):
1. eval varval="\$" + "$varname" # This substitution resolved in eval statement
2. .................. "$myvarname_a" # $myvarname_a previously resolved by for-loop
3. .................. "a" # ... to this value
4. eval "varval=$a" # This requires one more parsing step
5. eval varval="User-provided" # Final result of parsing (eval executes this)
Sobald das gesamte Parsen abgeschlossen ist, wird das Ergebnis ausgeführt, und seine Wirkung ist offensichtlich. Dies zeigt, dass an sich nichts besonders Geheimnisvolles eval
ist und die Komplexität im Parsen des Arguments liegt.
varval="User-provided"
Der verbleibende Code im obigen Beispiel testet einfach, ob der $ varval zugewiesene Wert null ist, und fordert den Benutzer in diesem Fall auf, einen Wert anzugeben.
$($n)
läuft$n
in einer Unterschale. Es wird versucht, den1
nicht vorhandenen Befehl auszuführen .