Gibt es etwas Ähnliches wie Pipefail für mehrere Befehle, wie eine 'try'-Anweisung, aber innerhalb von bash. Ich möchte so etwas machen:
echo "trying stuff"
try {
command1
command2
command3
}
Wenn ein Befehl fehlschlägt, können Sie den Fehler dieses Befehls jederzeit beenden und wiederholen. Ich möchte nicht so etwas tun müssen wie:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
Und so weiter ... oder so ähnlich:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Weil sich die Argumente jedes Befehls, von dem ich glaube (korrigieren Sie mich, wenn ich falsch liege), gegenseitig stören. Diese beiden Methoden scheinen mir schrecklich langwierig und böse zu sein, deshalb appelliere ich hier an eine effizientere Methode.
set -euo pipefail
.set -e
ist eine schreckliche Idee. In den Übungen in BashFAQ # 105 werden nur einige der unerwarteten Randfälle erläutert , die eingeführt werden, und / oder der Vergleich zeigt Inkompatibilitäten zwischen den Implementierungen verschiedener Shells (und Shell-Versionen) unter inulm.de/~mascheck/various/set -e .Antworten:
Sie können eine Funktion schreiben, die den Befehl für Sie startet und testet. Es sei angenommen ,
command1
undcommand2
sind Umgebungsvariablen , die auf einen Befehl gesetzt wurden.quelle
$*
, es schlägt fehl, wenn Argumente Leerzeichen enthalten. Verwenden Sie"$@"
stattdessen. Fügen Sie in ähnlicher Weise$1
die Anführungszeichen imecho
Befehl ein.test
da dies ein eingebauter Befehl ist.ls
. Wenn Siels foo
das Formular aufrufen und eine Fehlermeldung erhaltenls: foo: No such file or directory\n
, verstehen Sie das Problem. Wenn Sie stattdessen bekommen, werdenls: foo: No such file or directory\nerror with ls\n
Sie von überflüssigen Informationen abgelenkt. In diesem Fall ist es leicht zu argumentieren, dass der Überfluss trivial ist, aber er wächst schnell. Prägnante Fehlermeldungen sind wichtig. Noch wichtiger ist jedoch, dass diese Art von Wrapper auch Autoren dazu ermutigt, gute Fehlermeldungen vollständig wegzulassen.Was meinst du mit "aussteigen und den Fehler wiederholen"? Wenn Sie möchten, dass das Skript beendet wird, sobald ein Befehl fehlschlägt, tun Sie dies einfach
zu Beginn des Skripts (beachten Sie jedoch die Warnung unten). Machen Sie sich nicht die Mühe, die Fehlermeldung zu wiederholen: Lassen Sie den fehlerhaften Befehl damit umgehen. Mit anderen Worten, wenn Sie dies tun:
und command2 schlägt fehl, während eine Fehlermeldung an stderr gedruckt wird, dann scheint es, dass Sie erreicht haben, was Sie wollen. (Es sei denn, ich interpretiere falsch, was Sie wollen!)
Folglich muss sich jeder Befehl, den Sie schreiben, gut verhalten: Er muss Fehler an stderr anstelle von stdout melden (der Beispielcode in der Frage gibt Fehler an stdout aus) und er muss mit einem Status ungleich Null beendet werden, wenn er fehlschlägt.
Ich halte dies jedoch nicht mehr für eine gute Praxis.
set -e
hat seine Semantik mit verschiedenen Versionen von bash geändert, und obwohl es für ein einfaches Skript gut funktioniert, gibt es so viele Randfälle, dass es im Wesentlichen unbrauchbar ist. (Betrachten Sie Dinge wie:set -e; foo() { false; echo should not print; } ; foo && echo ok
Die Semantik hier ist etwas vernünftig, aber wenn Sie Code in eine Funktion umgestalten, die sich auf die Optionseinstellung stützt, um vorzeitig zu beenden, können Sie leicht gebissen werden.) IMO ist es besser zu schreiben:oder
quelle
trap some_func 0
wirdsome_func
am Ausgang ausgeführt)|| exit
nach jedem Befehl explizit zu schreiben .Ich habe eine Reihe von Skriptfunktionen, die ich auf meinem Red Hat-System häufig verwende. Sie verwenden die Systemfunktionen von
/etc/init.d/functions
, um grüne[ OK ]
und rote[FAILED]
Statusanzeigen zu drucken .Sie können die
$LOG_STEPS
Variable optional auf einen Protokolldateinamen setzen, wenn Sie protokollieren möchten, welche Befehle fehlschlagen.Verwendung
Ausgabe
Code
quelle
Für das, was es wert ist, ist eine kürzere Möglichkeit, Code zu schreiben, um jeden Befehl auf Erfolg zu überprüfen:
Es ist immer noch langweilig, aber zumindest lesbar.
quelle
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Eine Alternative besteht einfach darin, die Befehle zusammenzufügen,
&&
sodass der erste Fehler die Ausführung des Restes verhindert:Dies ist nicht die Syntax, nach der Sie in der Frage gefragt haben, aber es ist ein allgemeines Muster für den von Ihnen beschriebenen Anwendungsfall. Im Allgemeinen sollten die Befehle für Druckfehler verantwortlich sein, damit Sie dies nicht manuell tun müssen (möglicherweise mit einem
-q
Flag, um Fehler auszuschalten, wenn Sie sie nicht möchten). Wenn Sie die Möglichkeit haben, diese Befehle zu ändern, würde ich sie so bearbeiten, dass sie bei einem Fehler schreien, anstatt sie in etwas anderes zu verpacken, das dies tut.Beachten Sie auch, dass Sie nicht tun müssen:
Sie können einfach sagen:
Wenn Sie die Rückkehrcodes überprüfen müssen, verwenden Sie einen arithmetischen Kontext anstelle von
[ ... -ne
:quelle
Verwenden Sie Folgendes
set -e
, anstatt Runner-Funktionen zu erstellen oder zu verwendentrap
:Der Trap hat sogar Zugriff auf die Zeilennummer und die Befehlszeile des Befehls, der ihn ausgelöst hat. Die Variablen sind
$BASH_LINENO
und$BASH_COMMAND
.quelle
trap - ERR
schalten Sie die Falle am Ende des "Blocks" aus.Persönlich bevorzuge ich einen leichten Ansatz, wie hier zu sehen ;
Anwendungsbeispiel:
quelle
quelle
$*
, es schlägt fehl, wenn Argumente Leerzeichen enthalten. Verwenden Sie"$@"
stattdessen. (Obwohl $ * imecho
BefehlIch habe eine nahezu fehlerfreie Try & Catch-Implementierung in Bash entwickelt, mit der Sie Code schreiben können wie:
Sie können die Try-Catch-Blöcke sogar in sich selbst verschachteln!
Der Code ist Teil meines Bash Boilerplate / Frameworks . Es erweitert die Idee des Try & Catch weiter um Dinge wie Fehlerbehandlung mit Backtrace und Ausnahmen (plus einige andere nette Funktionen).
Hier ist der Code, der nur für try & catch verantwortlich ist:
Fühlen Sie sich frei zu verwenden, zu teilen und beizutragen - es ist auf GitHub .
quelle
Es tut mir leid, dass ich zur ersten Antwort keinen Kommentar abgeben kann. Sie sollten jedoch eine neue Instanz verwenden, um den Befehl auszuführen: cmd_output = $ ($ @)
quelle
Für Benutzer von Fischschalen , die über diesen Thread stolpern.
Sei
foo
eine Funktion, die keinen Wert "zurückgibt" (echo), sondern den Exit-Code wie gewohnt setzt.Um eine Überprüfung
$status
nach dem Aufrufen der Funktion zu vermeiden , haben Sie folgende Möglichkeiten :Und wenn es zu lang ist, um in eine Zeile zu passen:
quelle
Wenn ich benutze, muss
ssh
ich zwischen Problemen unterscheiden, die durch Verbindungsprobleme verursacht werden, und Fehlercodes des Remote-Befehls imerrexit
(set -e
) -Modus. Ich benutze die folgende Funktion:quelle
Sie können die großartige Lösung von @ john-kugelman verwenden, die Sie oben auf Nicht-RedHat-Systemen gefunden haben, indem Sie diese Zeile in seinem Code auskommentieren :
Fügen Sie dann am Ende den folgenden Code ein. Vollständige Offenlegung: Dies ist nur ein direktes Kopieren und Einfügen der relevanten Bits der oben genannten Datei aus Centos 7.
Getestet unter MacOS und Ubuntu 18.04.
quelle
Status auf funktionale Weise prüfen
Verwendungszweck:
Ausgabe
quelle