Anruferbewusste Funktion in Bash

Anruferbewusste Funktion in Bash

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 evalvom 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 callerintegrierte Programm aufrufe und dessen Ausgabe analysiere.

Alle diese Lösungen erscheinen sehr umständlich, daher habe ich zwei Fragen:

  1. 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

?

  1. 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 faktualisiert gdie $aVariable, da sie von aufgerufen wird g.

Dies steht im Gegensatz zu den mit typesetin deklarierten Variablen, die mit der kshSyntax ( function f { ...; }) in deklariert werden, oder mit in ksh93deklarierten Variablen, wo Sie Folgendes erhalten würden:privatezsh

f: 2
g: 1
global: 2

In diesem Fall müssen Sie nichts tun.

Um den Namen der Funktion zu erfahren, die Sie aufruft, bashkö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_assignmentsvon 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.

verwandte Informationen