In Python können wir Funktionen mit Code dekorieren, der automatisch angewendet und für Funktionen ausgeführt wird.
Gibt es eine ähnliche Funktion in Bash?
In dem Skript, an dem ich gerade arbeite, habe ich ein Boilerplate, das die erforderlichen Argumente testet und beendet, wenn sie nicht vorhanden sind - und einige Meldungen anzeigt, wenn das Debugging-Flag angegeben ist.
Leider muss ich diesen Code in jede Funktion erneut einfügen und wenn ich ihn ändern möchte, muss ich jede Funktion ändern.
Gibt es eine Möglichkeit, diesen Code aus jeder Funktion zu entfernen und auf alle Funktionen anzuwenden, ähnlich wie bei Dekoratoren in Python?
typeset
notwendig? Würde es es nicht anders erklären?eval "_inner_$(typeset -f x)"
erstellt_inner_x
als exakte Kopie des Originalsx
(wiefunctions[_inner_x]=$functions[x]
inzsh
).return
.Ich habe bereits mehrmals darüber gesprochen, wie und warum die folgenden Methoden funktionieren, damit ich es nicht noch einmal mache. Persönlich sind meine eigenen Favoriten zum Thema hier und hier .
Wenn Sie sich bei der Lektüre nicht interessiert , aber immer noch neugierig nur verstehen , dass die hier docs , die Funktion des Eingangs angebracht sind für Shell - Erweiterung ausgewertet , bevor die Funktion ausgeführt wird , und dass sie neu erzeugt in dem Zustand , sie waren , als die Funktion definiert wurde jedes Mal, wenn die Funktion aufgerufen wird.
ERKLÄREN
Sie benötigen lediglich eine Funktion, die andere Funktionen deklariert.
STARTE ES
Hier rufe ich
_fn_init
auf, mir eine aufgerufene Funktion zu deklarierenfn
.ERFORDERLICH
Wenn ich diese Funktion aufrufen möchte, stirbt sie, sofern die Umgebungsvariable nicht festgelegt
_if_unset
ist.Bitte beachten Sie die Reihenfolge der Shell-Traces - der
fn
Fehler schlägt nicht nur fehl, wenn er aufgerufen wird, wenn er nicht festgelegt_if_unset
ist, sondern er wird überhaupt nicht ausgeführt . Dies ist der wichtigste Faktor, den Sie bei der Arbeit mit Here-Document-Erweiterungen verstehen sollten - sie müssen immer zuerst auftreten, weil sie es schließlich sind<<input
.Der Fehler tritt auf,
/dev/fd/4
weil die übergeordnete Shell diese Eingabe auswertet, bevor sie an die Funktion übergeben wird. Dies ist die einfachste und effizienteste Methode zum Testen der erforderlichen Umgebung.Auf jeden Fall kann der Fehler leicht behoben werden.
FLEXIBEL
Die Variable
common_param
wird bei der Eingabe für jede von deklarierte Funktion auf einen Standardwert ausgewertet_fn_init
. Dieser Wert kann aber auch in jeden anderen geändert werden, der auch von jeder ähnlich deklarierten Funktion berücksichtigt wird. Ich werde jetzt die Spuren der Muschel weglassen - wir betreten hier kein Neuland oder so.Oben deklariere ich zwei Funktionen und setze
_if_unset
. Bevor ich eine der beiden Funktionen aufrufe, werde ich sie deaktivieren,common_param
damit Sie sehen können, dass sie sie selbst einstellen, wenn ich sie aufrufe.Und jetzt aus dem Bereich des Anrufers:
Aber jetzt möchte ich, dass es etwas ganz anderes ist:
Und wenn ich abgesetzt habe
_if_unset
?RESET
Wenn Sie den Status der Funktion jederzeit zurücksetzen müssen, ist dies problemlos möglich. Sie müssen nur tun (innerhalb der Funktion):
Ich habe die Argumente, mit denen die Funktion ursprünglich deklariert wurde, im
5<<\RESET
Eingabedateideskriptor gespeichert. So.dot
Sourcing , dass jederzeit in der Shell den Vorgang wiederholen , die es bis in die erste Stelle gesetzt. Es ist wirklich alles ziemlich einfach und ziemlich vollständig portierbar, wenn Sie die Tatsache übersehen möchten, dass POSIX die Dateikonstruktions-Geräteknotenpfade (die für die Shell erforderlich sind) nicht wirklich spezifiziert.dot
.Sie können dieses Verhalten leicht erweitern und verschiedene Zustände für Ihre Funktion konfigurieren.
MEHR?
Das kratzt übrigens kaum an der Oberfläche. Ich verwende diese Techniken oft, um kleine Hilfsfunktionen, die jederzeit deklarierbar sind, in die Eingabe einer Hauptfunktion einzubetten - zum Beispiel für zusätzliche Positionsarrays
$@
nach Bedarf. In der Tat - wie ich glaube, muss es etwas sehr Nahes sein, das die Muscheln höherer Ordnung sowieso tun. Sie können sehen, dass sie sehr einfach programmgesteuert benannt werden.Ich deklariere auch gerne eine Generatorfunktion, die einen begrenzten Parametertyp akzeptiert und dann eine Einweg- oder anderweitig bereichsbegrenzte Brennerfunktion nach dem Vorbild eines Lambda - oder einer Inline-Funktion - definiert, die sich einfach
unset -f
selbst ergibt, wenn durch. Sie können eine Shell-Funktion weitergeben.quelle
eval
?.dot
funktioniert mit Dateien und Streams, sodass Sie nicht auf die gleichen Probleme mit Argumentlisten stoßen, die Sie sonst möglicherweise hätten. Trotzdem ist es wahrscheinlich eine Frage der Präferenz. Ich denke auf jeden Fall, dass es sauberer ist - besonders wenn man sich mit der Bewertung befasst - das ist ein Albtraum von meinem Platz aus..dot
gut und bereit sind - oder jemals. Dies ermöglicht Ihnen ein wenig mehr Freiheit beim Testen der Bewertungen. Und es bietet die Flexibilität des Zustands bei der Eingabe - die auf andere Weise gehandhabt werden kann -, aber aus dieser Perspektive ist es weitaus weniger gefährlich als es isteval
.Ich denke, eine Möglichkeit, Informationen über die Funktion zu drucken, wenn Sie
ist, bash
return
und / oderexit
am Anfang jedes Skripts (oder in einer Datei, die Sie jedes Mal vor dem Ausführen des Programms beziehen) zu ändern . Also tippst duWenn Sie dies ausführen, erhalten Sie:
Das kann leicht mit dem Debugging-Flag aktualisiert werden, wenn Sie es brauchen, ungefähr so:
Diese Anweisung wird nur ausgeführt, wenn die Variable VERBOSE gesetzt ist (zumindest verwende ich so ausführlich in meinen Skripten). Es löst sicherlich nicht das Problem der Dekorationsfunktion, kann jedoch Meldungen anzeigen, falls die Funktion den Status ungleich Null zurückgibt.
Ebenso können Sie neu definieren
exit
, indem Sie alle Instanzen von ersetzenreturn
, wenn Sie das Skript beenden möchten.EDIT: Ich wollte hier die Art und Weise hinzufügen, wie ich Funktionen in Bash dekoriere, wenn ich viele davon habe und auch verschachtelte. Wenn ich dieses Skript schreibe:
Und für die Ausgabe kann ich das bekommen:
Es kann für jemanden hilfreich sein, der über Funktionen verfügt und diese debuggen möchte, um festzustellen, in welchem Funktionsfehler ein Fehler aufgetreten ist. Es basiert auf drei Funktionen, die im Folgenden beschrieben werden können:
Ich habe versucht, so viel wie möglich in Kommentare zu schreiben, aber hier ist auch die Beschreibung: Ich benutze die
_ ()
Funktion als Dekorateur, die ich nach der Deklaration jeder Funktion eingefügt habe :foo () { _
. Diese Funktion druckt den Funktionsnamen mit der richtigen Einrückung, je nachdem, wie tief die Funktion in einer anderen Funktion ist (als Standardeinrückung verwende ich 4 Leerzeichen). Normalerweise drucke ich dies in Grau, um dies vom üblichen Druck zu trennen. Wenn die Funktion mit oder ohne Argumente dekoriert werden muss, kann die vorletzte Zeile in der Dekorationsfunktion geändert werden.Um etwas innerhalb der Funktion zu drucken, habe ich eine
print ()
Funktion eingeführt , die alles, was an sie übergeben wird, mit dem richtigen Einzug druckt.Die Funktion
set_indentation_for_print_function
macht genau das, wofür sie steht, und berechnet die Einrückung aus dem${FUNCNAME[@]}
Array.Dieser Weg hat einige Fehler, zum Beispiel kann man keine Optionen übergeben, um sie zu
print
mögenecho
, zB-n
oder-e
, und auch wenn die Funktion 1 zurückgibt, ist sie nicht dekoriert. Und auch für Argumente, die anprint
mehr als die Terminalbreite übergeben werden und auf dem Bildschirm umbrochen werden, wird der Einzug für die umbrochene Zeile nicht angezeigt.Die beste Möglichkeit, diese Dekoratoren zu verwenden, besteht darin, sie in einer separaten Datei und in jedem neuen Skript abzulegen, um diese Datei zu erstellen
source ~/script/hand_made_bash_functions.sh
.Ich denke, der beste Weg, den Funktionsdekorator in bash zu integrieren, besteht darin, den Dekorator in den Körper jeder Funktion zu schreiben. Ich denke, es ist viel einfacher, Funktionen innerhalb von Funktionen in Bash zu schreiben, da es die Option gibt, alle Variablen global festzulegen, nicht wie in objektorientierten Standardsprachen. Das macht es so, als würden Sie in bash Beschriftungen um Ihren Code setzen. Zumindest hat mir das beim Debuggen von Skripten geholfen.
quelle
Vielleicht können Ihnen die Dekorationsbeispiele in http://sourceforge.net/projects/oobash/ project helfen (oobash / docs / examples / decorator.sh).
quelle
Für mich ist dies der einfachste Weg, ein Dekorationsmuster in bash zu implementieren.
quelle