Neben assoziativen Arrays gibt es in Bash verschiedene Möglichkeiten, dynamische Variablen zu erzielen. Beachten Sie, dass alle diese Techniken Risiken bergen, die am Ende dieser Antwort erläutert werden.
In den folgenden Beispielen gehe ich davon aus, dass i=37
und dass Sie die benannte Variable mit dem var_37
Anfangswert aliasisieren möchten lolilol
.
Methode 1. Verwenden einer Zeigervariablen
Sie können den Namen der Variablen einfach in einer Indirektionsvariablen speichern, ähnlich wie bei einem C-Zeiger. Bash hat dann eine Syntax zum Lesen der Alias-Variablen: Erweitert ${!name}
sich auf den Wert der Variablen, deren Name der Wert der Variablen ist name
. Sie können sich das als zweistufige Erweiterung ${!name}
vorstellen : erweitert sich zu $var_37
, erweitert sich zu lolilol
.
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
Leider gibt es keine Gegenstücksyntax für Ändern der Alias-Variablen. Stattdessen können Sie die Zuordnung mit einem der folgenden Tricks erreichen.
1a. Zuweisen miteval
eval
ist böse, aber auch der einfachste und tragbarste Weg, um unser Ziel zu erreichen. Sie müssen die rechte Seite der Aufgabe vorsichtig verlassen, da sie zweimal ausgewertet wird . Eine einfache und systematische Möglichkeit besteht darin, die rechte Seite vorher zu bewerten (oder zu verwenden printf %q
).
Und Sie sollten manuell überprüfen, ob die linke Seite ein gültiger Variablenname oder ein Name mit Index ist (was wäre, wenn dies der Fall wäre evil_code #
?). Im Gegensatz dazu erzwingen alle anderen unten aufgeführten Methoden dies automatisch.
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
Nachteile:
- überprüft nicht die Gültigkeit des Variablennamens.
eval
ist böse
eval
ist böse
eval
ist böse
1b. Zuweisen mitread
Mit dem integrierten read
Programm können Sie einer Variablen, deren Namen Sie geben, Werte zuweisen. Diese Tatsache kann in Verbindung mit Here-Strings ausgenutzt werden:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
Das IFS
Teil und die Option -r
stellen sicher, dass der Wert unverändert zugewiesen wird, während die Option -d ''
das Zuweisen mehrzeiliger Werte ermöglicht. Aufgrund dieser letzten Option wird der Befehl mit einem Exit-Code ungleich Null zurückgegeben.
Beachten Sie, dass, da wir eine Here-Zeichenfolge verwenden, ein Zeilenumbruchzeichen an den Wert angehängt wird.
Nachteile:
- etwas dunkel;
- kehrt mit einem Exit-Code ungleich Null zurück;
- Hängt eine neue Zeile an den Wert an.
1c. Zuweisen mitprintf
Seit Bash 3.1 (veröffentlicht 2005) kann das printf
eingebaute Ergebnis auch einer Variablen zugewiesen werden, deren Name angegeben ist. Im Gegensatz zu den vorherigen Lösungen funktioniert es einfach, es ist kein zusätzlicher Aufwand erforderlich, um den Dingen zu entkommen, eine Aufteilung zu verhindern und so weiter.
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
Nachteile:
- Weniger tragbar (aber gut).
Methode 2. Verwenden einer Referenzvariablen
Seit Bash 4.3 (veröffentlicht 2014) verfügt das integrierte declare
Programm über eine Option -n
zum Erstellen einer Variablen, die eine „Namensreferenz“ zu einer anderen Variablen darstellt, ähnlich wie C ++ - Referenzen. Genau wie in Methode 1 speichert die Referenz den Namen der Alias-Variablen, aber jedes Mal, wenn auf die Referenz zugegriffen wird (entweder zum Lesen oder zum Zuweisen), löst Bash die Indirektion automatisch auf.
Darüber hinaus verfügt Bash über eine spezielle und sehr verwirrende Syntax, um den Wert der Referenz selbst zu ermitteln ${!ref}
.
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
Dies vermeidet nicht die unten erläuterten Fallstricke, macht aber zumindest die Syntax einfach.
Nachteile:
Risiken
Alle diese Aliasing-Techniken bergen verschiedene Risiken. Der erste führt jedes Mal beliebigen Code aus, wenn Sie die Indirektion auflösen (entweder zum Lesen oder zum Zuweisen) . Anstelle eines skalaren Variablennamens wie var_37
können Sie auch einen Array-Index wie verwenden arr[42]
. Bash wertet den Inhalt der eckigen Klammern jedoch jedes Mal aus, wenn er benötigt wird, sodass Aliasing arr[$(do_evil)]
unerwartete Auswirkungen hat. Verwenden Sie diese Techniken daher nur, wenn Sie die Herkunft des Alias steuern .
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
Das zweite Risiko besteht darin, einen zyklischen Alias zu erstellen. Da Bash-Variablen durch ihren Namen und nicht durch ihren Bereich identifiziert werden, können Sie versehentlich einen Alias für sich selbst erstellen (während Sie denken, dass dies eine Variable aus einem umschließenden Bereich alias wäre). Dies kann insbesondere bei Verwendung allgemeiner Variablennamen (wie var
) geschehen . Als Folge verwendet nur diese Techniken , wenn Sie den Namen des Alias - Variable steuern .
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
Quelle: