Ich habe ein Bash-Skript, das verwendet, set -o errexit
damit bei einem Fehler das gesamte Skript zum Zeitpunkt des Fehlers beendet wird.
Das Skript führt einen curl
Befehl aus, mit dem die gewünschte Datei manchmal nicht abgerufen werden kann. In diesem Fall tritt jedoch kein Fehler beim Beenden des Skripts auf.
Ich habe eine for
Schleife hinzugefügt
- Machen Sie eine kurze Pause und wiederholen Sie den
curl
Befehl - Verwenden Sie
false
am Ende der for-Schleife, um einen Standard-Ausgangsstatus ungleich Null zu definieren. Wenn der curl-Befehl erfolgreich ausgeführt wurde, wird die Schleife unterbrochen und der Ausgangsstatus des letzten Befehls sollte Null sein.
#! /bin/bash
set -o errexit
# ...
for (( i=1; i<5; i++ ))
do
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
if [ -f ~/.vim/autoload/pathogen.vim ]
then
echo "file has been retrieved by curl, so breaking now..."
break;
fi
echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
sleep 5
# exit with non-zero status so main script will errexit
false
done
# rest of script .....
Das Problem ist, wenn der curl
Befehl fehlschlägt, die Schleife den Befehl fünfmal wiederholt - wenn alle Versuche erfolglos sind, wird die for-Schleife beendet und das Hauptskript wird fortgesetzt -, anstatt den Befehl auszulösen errexit
.
Wie kann ich das gesamte Skript beenden, wenn diese curl
Anweisung fehlschlägt?
quelle
true
die break-Anweisung explizit voranzustellen und den Exit-Wert der Schleife sicherzustellen?exit 1
als es einfach geklapptexit
hätte. Es ist jedoch eine Frage des Stils und andere können ihre eigene Meinung haben.exit
als normales beenden lesen - das beendet das script von selbst.exit 1
würde mir als "signal" für einen anderen prozess (dherrexit
) vorlesen, dass er das script basierend auf dem "ergebnis" von beenden sollexit 1
. - also bin ichexit
exit 1
. Das wirkt sich überhaupt nicht auserrexit
. Es teilt dem aufrufenden Programm lediglich mit, dass ein Fehler aufgetreten ist. Derfalse
Befehl enthält eine Anweisung:exit(1)
. 99,9% der Unix-Befehle geben bei Erfolg 0 und bei Fehlern ungleich Null zurück. Deines sollte auch.Wenn Sie festgelegt haben
errexit
, sollte diefalse
Anweisung das Skript sofort beenden. Dasselbe gilt, wenn dercurl
Befehl fehlgeschlagen ist.Ihr Beispielskript sollte, wie geschrieben, nach dem ersten
curl
fehlgeschlagenen Befehl beim erstenfalse
Aufruf beendet werden, wenn errexit gesetzt ist.Um zu sehen, wie es funktioniert (ich benutze die Abkürzung
-e
fürerrexit
:Wenn der
curl
Befehl also mehrmals ausgeführt wird, hat dieses Skript keineerrexit
festgelegt.quelle
set -e
ist subtiler als das. Es wird nicht nach dem ersten fehlgeschlagenen Befehl in einer Schleife beendet. Sie können sich das selbst beweisen,(set -e; for (( i=1; i<5; i++ )); do echo $i; false; done || echo "FAIL"; )
indem Sie ausführen und feststellen, dass der Codefalse
viermal ausgeführt wird. Weitere Informationenset -e
finden Sie in Gregs FAQ Nr. 105 .errexit
nicht gesetzt wurde. Bitte wenden Sie die Logik auf das Skript in der Frage an. Führen Sie Folgendes aus :(set -e; for (( i=1; i<5; i++ )); do echo $i; false; done ; echo still here )
Ja. Das Testen von Rückgabewerten mitif
while
||
&&
etc löst kein Errexit aus. Das ursprüngliche Skript hat||
die for-Schleife nicht ausgeführt.set -o errexit
Befehl in meinem Beispielcode nicht angezeigt habe, ich habe ihn jetzt hinzugefügt - und für mich war es kein Fehler, wie erwartet zu beenden. Ich musste denfalse
als letzten Befehl in der for-Schleife behalten , dann die Schleife schließen mitdone || exit [1]
- dann hat es gut funktioniert!set -o errexit
kann in Schleifen und Unterschalen schwierig sein, weil Sie den Weg zurück aus dem Prozess passieren müssen.Das Unterbrechen einer Schleife (auch im normalen Betrieb) wird als schlechte Praxis angesehen. Sie können mich Old-School nennen, um eine while-Schleife anstelle einer for-Schleife für zwei Bedingungen zu bevorzugen, aber ich finde es besser zu lesen:
quelle
Wenn
errexit
gesetzt ist und dercurl
Befehl fehlschlägt, wird das Skript direkt nach dem fehlgeschlagenen Einrollbefehl beendet. Im Bash-Handbuch gibt es keinen Hinweis, der denset -e
fehlgeschlagenen Rückgabestatus eines einzelnen Befehls in einem zusammengesetzten Befehl ignoriert. Dies ist nur der Fall, wenn der zusammengesetzte Befehl in einem Kontext ausgeführt wird, in dem erset -e
ignoriert wird.https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
Probieren Sie ein leicht angepasstes Beispiel von RobertL. Dies stoppt bei der ersten Wiederholung direkt nach dem falschen Befehl:
quelle
Sie können einfach die Option --fail zum Befehl curl hinzufügen. Dadurch wird Ihr Problem behoben. Das Skript schlägt fehl und wird bei einem Fehler beendet, wenn der Befehl curl fehlschlägt.
quelle