Ich habe eine Funktion (=callee), die effektiv einige Variablen in ihrem Anrufer deklarieren und zuweisen sollte. Sie sollte auch in der Lage sein, den Namen ihres Anrufers zu erkennen.
Ersteres erreiche ich derzeit, indem ich in eine übergebene Variable einen String zurückschreibe, der eval
vom Anrufer verarbeitet werden kann.
write_local_var_assignments variable_name; eval "$variable_name"
Ich stelle mir vor, dass ich Letzteres erreichen kann, indem ich entweder den Anrufer passieren lasse "$FUNCNAME"
oder indem ich den Angerufenen das caller
integrierte Programm aufrufe und dessen Ausgabe analysiere.
Alle diese Lösungen erscheinen sehr umständlich, daher habe ich zwei Fragen:
- Ist es dem Angerufenen möglich, lokale Variablen dem Kontext des Anrufers zuzuweisen, ohne dass dieser mitarbeiten muss?
D. h., kann ich Folgendes komprimieren:
write_local_var_assignments variable_name; eval "$variable_name"
in nur
run_local_var_assignments
?
- Gibt es eine bessere Möglichkeit, den Namen des Aufrufers einer Funktion zu erhalten? Es wäre schön, das Ergebnis direkt ohne Parsen oder Befehlsersetzung zu erhalten.
Antwort1
In bash
(und ksh88
, mksh
, yash
, dash
, zsh
) ist der Gültigkeitsbereich der lokalen Variablen dynamisch.
Dieser Code:
f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"
erzeugt diese Ausgabe:
f: 2
g: 2
global: 0
Dies f
aktualisiert g
die $a
Variable, da sie von aufgerufen wird g
.
Dies steht im Gegensatz zu den mit typeset
in deklarierten Variablen, die mit der ksh
Syntax ( function f { ...; }
) in deklariert werden, oder mit in ksh93
deklarierten Variablen, wo Sie Folgendes erhalten würden:private
zsh
f: 2
g: 1
global: 2
In diesem Fall müssen Sie nichts tun.
Um den Namen der Funktion zu erfahren, die Sie aufruft, bash
können Sie in verwenden${FUNCNAME[1]}
.
Das zsh
Äquivalent ist $funcstack[2]
:
$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g
Antwort2
Wenn Ihnen die Verwendung einer Befehlsersetzung nichts ausmacht, gibt es eine Funktion, mit der Sie selbstausgewertete Zeichenfolgen an Ihren Anrufer zurückgeben können, sodass Sie eine run_local_var_assignments
von Ihnen erstellte Funktion folgendermaßen aufrufen können:
$(run_local_var_assignments)
Die Funktion sieht folgendermaßen aus:
emit () {
local IFS=$'\n'
printf 'eval eval %q' "$*"
}
Es gibt eine Eval-Anweisung aus, die Ihrer eval "$variable_name"
oben sehr ähnlich ist. Da dies das erste Element in der resultierenden Zeile ist, führt Bash die ausgegebene Eval-Anweisung als Befehl aus.
Die von der Funktion durchgeführte doppelte Auswertung und das %q-Escapen sind einfach vorhanden, weil sie erforderlich sind, um Zeilenumbrüche und andere Sonderzeichen korrekt an den Anrufer weiterzugeben.
Ihre Funktion könnte dann wie folgt geschrieben werden:
run_local_var_assignments () {
local assignments=()
assignments+=( 'local myvar1="my value1"' )
assignments+=( 'local myvar2="my value2"' )
emit "${assignments[@]}"
}
Die Zuweisungen werden genauso geschrieben, als ob es sich um Quellcode handeln würde, daher sind beispielsweise in den Werten Anführungszeichen wie oben erforderlich.
Wenn Sie diese Funktion in einer Befehlsersetzung aufrufen (wie oben in dieser Antwort), werden diese lokalen Variablen mit diesen Werten im Gültigkeitsbereich des Anrufers erstellt.
Sie könnten statt eines Anweisungs-Arrays auch einen normalen String oder ein Heredoc verwenden, aber ich finde die Array-Methode häufig nützlich, um dynamisch einen Anweisungsblock aufzubauen, also dachte ich, ich zeige das, auch wenn es etwas komplizierter ist.