Ist es möglich, Indirektion zum Setzen von Variablen zu verwenden?

7

ZB wird das nächste Skript Helloaufgrund der ${!Regel gedruckt .

A=B
B=Hello
echo ${!A}

Aber wie kann ich den Wert auf eine Variable mit indirektem Namen setzen? Die einfache Verwendung von !Zeichen funktioniert nicht:

A=B
!A=Hello # here is tricky line
echo $B

Ich weiß, dass es einen Trick bei der Verwendung von temporären Dateien gibt, aber ich bin daran interessiert, Indirektion zu verwenden, nicht so etwas

A=B
echo "$A=Hello" > 1.tmp
. 1.tmp
echo $B
Sergey Grinev
quelle
2
eval $A=foobar
Forcefsck
Möchten Sie Synonyme erstellen, damit beide Variablen auf dieselben Daten verweisen? Mit anderen Worten, wenn sich einer ändert, ändert sich der andere? Ich bin mir nicht sicher, ob ich deine Absicht hier verstehe.
Christian
1
Nein, ich wollte nur einmal eine Variable durch den Wert einer anderen setzen
Sergey Grinev

Antworten:

5

Ein anderer Weg, den Sie versuchen könnten:

$ A=B
$ read $A <<< 81
$ echo "$B"
81

Aber es gibt ein Sicherheitsrisiko (!) Wie bei all diesen Methoden (auch declare/ typesetund natürlich eval). In diesem Fall muss man die linke Seite (den Wert von $A) kontrollieren , mit anderen Worten, zumindest in bashsollte Variable $Anicht enthalten Benutzergesteuerte Eingabe, zum Beispiel eine Eingabedatei usw.


Wenn Ihre Shell keine Here-Zeichenfolge ( <<<) unterstützt, können Sie stattdessen auch ein Here-Dokument verwenden:

A=B
read $A << EOF
82
EOF
echo "$B"
Scrutinizer
quelle
wow, aber warum funktioniert es?
Sergey Grinev
2
Die Shell erweitert die Variable $ A vor dem
Scrutinizer
Das einzige Problem: Es funktioniert nicht unter Solaris
Sergey Grinev
1
Sie könnten ein Here-Dokument anstelle eines Here-Strings versuchen, siehe die Bearbeitung in meinem Beitrag.
Scrutinizer
2
Dies ist keine gute Methode. Es ist komplex und bricht ab, wenn der Wert Sonderzeichen enthält (Zeilenumbrüche, umgekehrte Schrägstriche und führende oder nachfolgende Leerzeichen in einer Zeile sind problematisch).
Gilles 'SO - hör auf böse zu sein'
11

Sie können dies mit dem typeseteingebauten tun:

$ A=B
$ typeset $A=42
$ echo $B
42

Oder mit declare:

$ declare $A=1
$ echo $B
1
Matte
quelle
1
In bash, typesetist veraltet und sein Ersatz ist declare. Sie sind mehr oder weniger identisch.
jw013
1
typesetist ein bisschen portabler (funktioniert in ksh)
Mat
Vielen Dank! Ich kann mich nicht entscheiden, welche Antwort besser ist. Wirf eine Münze :)
Sergey Grinev
8

Nach der Indirektion beim Abrufen des Werts einer Variablen

Der tragbare Weg ist zu verwenden eval. Sie müssen auf das Anführungszeichen achten, damit Sonderzeichen im Wert nicht ausgewertet werden, wenn dies nicht der Fall sein sollte. Am einfachsten ist es, den neuen Wert in einer Zwischenvariablen zu speichern und den Wert dieser Variablen zuzuweisen.

A=B
tmp='stuff with special characters or whatever…'
eval $A=\$tmp

Das Argument dafür evalist B=$tmpeine einfache Zuordnung. Sie benötigen keine doppelten Anführungszeichen, $tmpauch wenn es Leerzeichen oder Globbing-Zeichen enthält, da diese in einer Zuweisung nicht erweitert werden.

Wenn Sie eine Umgebungsvariable erstellen möchten, können Sie verwenden export. In bash oder ksh oder zsh können Sie typeseteine lokale Variable erstellen (dies declaregilt auch für bash). Auch hier sollten Sie eine Zwischenvariable verwenden, damit Sonderzeichen nicht entstellt werden. Beachten Sie, dass Sie außer in zsh doppelte Anführungszeichen um die Variablenerweiterung setzen müssen, da dies keine Zuweisung ist, sondern eine integrierte Funktion, die ein Argument verwendet, das zufällig wie eine Zuweisung aussieht.

export $A="$tmp"
Gilles 'SO - hör auf böse zu sein'
quelle
Dies evalist die richtige Antwort. Es ist portabel, entspricht dem POSIX-Standard und wird überall in fortgeschritteneren Shell-Skripten verwendet. Eines jedoch: Die Verwendung exportin derselben Zeile wie die Zuweisung ist nicht portierbar.
MadScientist
@ MadScientist export var=valueist POSIX. Es ist nicht für Bourne-Shells tragbar, aber es handelt sich um eine verschwindende Rasse (verwenden Sie nur /usr/xpg4/bin/shoder /usr/xpg6/bin/shoder ksh und nicht /bin/shauf älteren Solaris-Computern).
Gilles 'SO - hör auf böse zu sein'