Gibt es so etwas wie Closures für zsh?

Gibt es so etwas wie Closures für zsh?

Ich habe mich gerade dazu entschlossen, zsh auszuprobieren (über oh-my-zsh), und spiele jetzt damit herum, precmdeine zweizeilige Eingabeaufforderung zu emulieren, die in mehr als nur der letzten Zeile richtige Eingabeaufforderungen enthält.

Also klone ich das Standardthema und inspirierte mich vondieser Beitrag(das ich auch viel zum Lernen verwende), ich mache so etwas (Farben füge ich später hinzu):

function precmd {
    local cwd="${(%):-[%~]}"
    local who_where="${(%):-%n@%m}"
    local git_info=${(%)$(git_prompt_info)}
    local right_prompt="     $git_info [$who_where]"
    local left_prompt="${(r:(($COLUMNS - ${#${right_prompt}})):: :)cwd}"

    echo "$left_prompt$right_prompt"
}

Und es funktioniert. Aber ich frage mich: Definiert zsh all diese Variablen jedes Mal, wenn precmd aufgerufen wird?

Ich habe nach Closures, Scope und Namespaces in Bezug auf zsh gegoogelt und versucht, die lokalen Variablen als Daten an precmd anzuhängen, damit die Variablen nicht jedes Mal neu definiert werden müssen, aber ich habe nichts gefunden. Gibt es eine Möglichkeit, das zu tun, was ich versuche, oder sollte ich es einfach lassen?

Als Randbemerkung, und nur wenn es damit zusammenhängt: Was bedeutet „eine Funktion geladen haben“?

Antwort1

Zsh hat nichts wie Closures, Pakete oder Namespaces. Zsh fehlen eine Reihe von Dingen, die für echte Closures erforderlich sind:

  • Funktionen sind nicht erstklassig. Sie können Funktionen nicht als Argumente an andere Funktionen weitergeben und Funktionen können keine anderen Funktionen zurückgeben. (Sie können dieNameeiner aufzurufenden Funktion, aber das ist nicht dasselbe wie die Übergabe der Funktion selbst).

  • Sie können keine verschachtelten Funktionen haben. Alle Funktionen in zsh sind global. Sie müssen Ihren Funktionsnamen ein Präfix voranstellen, um Konflikte zu vermeiden. Beachten Sie insbesondere, dass Funktionen externe Programme mit demselben Namen überschatten. Wenn Sie eine Funktion namens haben ls, wird diese anstelle des Programms aufgerufen ls. Dies kann nützlich sein, außer wenn Sie es versehentlich tun.

  • Variablen haben einen dynamischen Gültigkeitsbereich, nicht einen statischen wie in den meisten modernen Sprachen. Selbst wenn Sie verschachtelte Funktionen haben könnten, würden innere Funktionen die lokalen Variablen äußerer Funktionen nicht wie erwartet schließen. Sie könnten sie nicht verwenden, um Module zu erstellen, wie dies beispielsweise in Javascript der Fall ist.

  • Zshtuthaben anonyme Funktionen, sind aber ohne diese anderen Dinge nicht sehr nützlich.

Das Beste, was Sie grundsätzlich tun können, ist, allen Ihren Funktionen und globalen Variablen ein Präfix zuzuweisen.

Ich möchte auch darauf hinweisen, dass Sie Ihre precmdwie folgt definieren sollten:

% autoload -Uz add-zsh-hook
% add-zsh-hook precmd my_precmd_function

add-zsh-hookermöglicht Ihnen, Ihre Funktion einzubinden, precmdohne dass andere Funktionen überschrieben werden, die ebenfalls einbinden möchten precmd.

Was es bedeutet, eine Funktion geladen zu haben, ist eine andere Frage. Zsh hat eine automatische Ladefunktion, die Funktionen nur dann von der Festplatte lädt, wenn sie tatsächlich aufgerufen werden. Wenn Sie dies tun autoload -Uz foobar, wird die genannte Funktion foobarzum Aufruf verfügbar. Wenn Sie tatsächlich aufrufen foobar, wird die Definition von der Festplatte geladen.

Antwort2

Nein, Closures sind zu kompliziert für zsh. Zsh ist darauf ausgelegt, kleine Skripte zu interpretieren, die nicht weit von direkter Interaktion entfernt sind. Es verfügt nicht über ausgefallene Sprachfunktionen, die für die Programmierung im Großen sehr nützlich sind, aber weniger für die Art kleiner Aufgaben, für die Shells normalerweise verwendet werden.

Beachten Sie: Wenn es eine Art Abschluss gäbe, der es ermöglichte, die Werte der Variablen ein für alle Mal vorab zu berechnen und dann zu speichern, würden die Werte nicht aktualisiert, wenn sich etwas ändert und die Informationen dadurch ungültig werden.

$git_infound die abgeleiteten Variablen können sich jederzeit aufgrund einer Änderung an einer in Git eingecheckten Datei oder am Git-Repository ändern. Sie müssen also ohnehin jedes Mal neu berechnet werden.

cwdSie könnten die Werte von und in einer globalen Variable zwischenspeichern who_where, da sie sich im Normalbetrieb nicht ändern. cwdändert sich, wenn sich das aktuelle Verzeichnis ändert, sodass es von aktualisiert werden müsste chpwd. Diese Variablen sind jedoch extrem schnell zu berechnen, sodass es keinen Sinn hat, sich darum zu kümmern. Die aufwendige Berechnung läuft hier git_prompt_info, und das kann sich jederzeit ändern.

Wenn Sie zwischen den einzelnen Befehlen Informationen anzeigen, ist es möglicherweise eine bessere Idee, diese als Teil der Eingabeaufforderung ( PS1oder des psvarArrays) zu platzieren. Zsh weiß, dass es die Eingabeaufforderung in verschiedenen Situationen erneut anzeigen muss, weiß jedoch nichts darüber, von was Sie drucken precmd.

Antwort3

Ja, diese Variablen werden bei jedem Aufruf der Funktion (neu) definiert.

Wenn Sie sie nur einmal initialisieren möchten, können Sie sie einfach auf die oberste Ebene aus der Funktion heraus verschieben.

Antwort4

Um Closures zu haben, muss die Sprache in der Lage sein, Funktionen als Elemente oder Objekte zu manipulieren, was in zsh nicht möglich ist, außer über Strings und das evalBuiltin (wie in den anderen Shells). Aber das ist sehr begrenzt, da Sie alle Low-Level-Sachen selbst handhaben müssen (z. B. Anführungszeichen). Wenn die Argumente jedoch keine Sonderzeichen haben (und man sich daher nicht um Anführungszeichen kümmern muss), ist es leicht möglich, einige einfache Dinge mit dieser Idee zu tun, und in zsh können anonyme Funktionen ein wenig helfen. Um beispielsweise Funktionen zu definieren, die berechnen a*x+b*y, wobei aund bKonstanten sind, die bei der Definition der Funktion angegeben wurden xund ydie Argumente der Funktion sind:

mk_ax_plus_by() { echo "() { echo \$((($1)*(\$1)+($2)*(\$2))) }" }

fct_2x_plus_3y=$(mk_ax_plus_by 2 3)
fct_5x_plus_7y=$(mk_ax_plus_by 5 7)

Man hat also eine Funktion fct_2x_plus_3y, die berechnet, 2*x+3*yund eine Funktion fct_5x_plus_7y, die berechnet 5*x+7*y(beachten Sie, dass ich die Funktionsnamen nur aus Gründen der Lesbarkeit gewählt habe. Es können beliebige Namen sein und Sie müssen den Inhalt nicht einmal in Variablen speichern). Beachten Sie auch, dass es sich dabei eigentlich um Zeichenfolgen handelt (keine Funktionen in der Shell-Terminologie), die sich jedoch wie Funktionen mit dem integrierten Befehl verhalten eval. Ein Anwendungsbeispiel:

% eval $fct_2x_plus_3y 4 9
35
% eval $fct_5x_plus_7y 4 9
83

as 2*4+3*9ergibt 35 und 5*4+7*9ergibt 83.

Beachten Sie, dass man hier im Gegensatz zu funktionalen Sprachen zwischen Parametern unterscheiden muss, die aus der Umgebung stammen (ohne Anführungszeichen) und Parametern der definierten Funktion (in Anführungszeichen), die also nur beim Funktionsaufruf ausgewertet werden. Es ist jedoch möglich, die Implementierung so zu ändern, dass sie funktionalen Sprachen ähnlicher ist, und die Verwendung bleibt dieselbe:

mk_ax_plus_by()
{
  local x='$1' y='$2'
  echo "() { echo \$((($1)*($x)+($2)*($y))) }"
}

verwandte Informationen