2012-03-30 9 views
22

Es scheint, dass der empfohlene Weg in bash indirekten variablen Einstellung zu tun, ist die Verwendung eval:Indirekte Variablenzuweisung in bash

var=x; val=foo 
eval $var=$val 
echo $x # --> foo 

Das Problem ist die übliche mit eval:

var=x; val=1$'\n'pwd 
eval $var=$val # bad output here 

(und Da es an vielen Stellen empfohlen wird, frage ich mich, wie viele Skripte sind deshalb anfällig ...)

In jedem Fall die offensichtliche Lösung von usin g (entkommen) Anführungszeichen funktioniert nicht wirklich:

var=x; val=1\"$'\n'pwd\" 
eval $var=\"$val\" # fail with the above 

Die Sache ist die, dass bash indirekten variablen Referenz in (mit ${!foo}) gebacken, aber ich sehe keine solche Art und Weise indirekte Zuordnung zu tun - ist Gibt es einen vernünftigen Weg, dies zu tun?

Für die Aufzeichnung habe ich eine Lösung finden, aber das ist nicht etwas, das ich betrachten würde „gesund“ ...:

eval "$var='"${val//\'/\'\"\'\"\'}"'" 

Antwort

2

Der wichtigste Punkt ist, dass der empfohlene Weg, dies zu tun ist:

eval "$var=\$val" 

mit der RHS indirekt auch gemacht. Da eval in der gleichen Umgebung verwendet wird, wird es $val gebunden haben, so verzögern es funktioniert, und seit jetzt ist es nur eine Variable. Da der $val Variable einen bekannten Namen hat, sind es keine Probleme mit der Quotierung, und es könnte sogar als geschrieben worden ist:

eval $var=\$val 

Aber da es besser ist, zu immer Zitate hinzuzufügen, ist die ehemalige besser oder sogar dies:

eval "$var=\"\$val\"" 

Eine bessere Alternative in der bash, die für die ganze Sache erwähnt wurden, dass vermeiden eval vollständig (und nicht so subtil wie declare usw.):

printf -v "$var" "%s" "$val" 

Obwohl dies keine direkte Antwort ist, was ich ursprünglich gefragt ...

13
eval "$var=\$val" 

Das Argument eval sollte immer eine einzige sein Zeichenfolge, die in einfache oder doppelte Anführungszeichen eingeschlossen ist. Sämtlicher Code, der von diesem Muster abweicht, weist in Randfällen, wie Dateinamen mit Sonderzeichen, unbeabsichtigtes Verhalten auf. Wenn das Argument eval durch die Shell erweitert wird, wird $var durch den Variablennamen ersetzt und \$ wird durch einen einfachen Dollar ersetzt. Der String, der ausgewertet wird, wird daher:

varname=$value 

Das ist genau das, was Sie wollen.

Normalerweise sollten alle Ausdrücke des Formulars $varname in doppelte Anführungszeichen eingeschlossen werden. Es gibt nur zwei Stellen, an denen die Anführungszeichen weggelassen werden können: variable Zuweisungen und case. Da es sich um eine variable Zuweisung handelt, werden die Anführungszeichen hier nicht benötigt. Sie tun nicht weh, obwohl, so konnte man auch den ursprünglichen Code wie schreiben:

eval "$var=\"the value is $val\"" 
+0

Der Indirektionsoperator auf der RHS ist nicht das, was ich suche. –

+1

(* Stirnschlag *) Bah, ich habe völlig vermisst, warum ich Indirection auf der RHS will. Da deine Antwort nicht darüber spricht, werde ich es jetzt bearbeiten, anstatt die Antwort-mich-und-pat-meine-eigene-zurück-zu machen ... –

+0

Fantastisch! In der Vergangenheit habe ich einen komplizierten Eve-Export-Quatsch gemacht. Vielen Dank. Für die Googler, gehen Sie mit der obigen Antwort, nicht das Eval-Export-Format. – bgStack15

24

Eine etwas bessere Art und Weise, die Vermeidung der möglichen Auswirkungen auf die Sicherheit der Verwendung von eval ist

declare $var="$val" 

Beachten Sie, dass declare ist ein Synonym für typeset in bash. Der typeset Befehl wird auf breiterer Ebene unterstützt (ksh und zsh verwenden es auch):

typeset $var="$val" 
+0

Dies sieht nicht tragbar zu Shells kleiner als Bash – MarcH

+0

In der Tat; während 'declare' eine Erweiterung des POSIX-Standards ist, ist es auch nur ein Synonym für' typeset', das * von anderen Major-Shells unterstützt wird ('ksh' und' zsh', nämlich). Shells, die etwas ähnliches nicht unterstützen, müssen 'eval' mit Vorsicht verwenden. – chepner

+3

'eval" $ var = '$ val' "' ist nicht annähernd vorsichtig genug: Wenn der Inhalt literale einfache Anführungszeichen enthält, können sie leicht entkommen. –

14

Bash hat eine Erweiterung printf, die das Ergebnis in eine Variable speichert:

printf -v "${VARNAME}" '%s' "${VALUE}" 

Dies verhindert, dass alle möglichen Flucht Probleme .

Wenn Sie eine ungültige Kennung für $VARNAME verwenden, wird der Befehl fehl und liefert Statuscode 2:

$ printf -v ';;;' foobar; echo $? 
bash: printf: `;;;': not a valid identifier 
2