Gibt es eine Möglichkeit, eine Funktion in meinem Bash-Skript bei einem Befehlsfehler automatisch ausführen zu lassen?


11

Ich schreibe ein Shell-Skript, das eine Reihe von Befehlen ausführen muss, und jeder Befehl hängt von jedem vorherigen Befehl ab. Wenn ein Befehl fehlschlägt, sollte das gesamte Skript fehlschlagen und ich rufe eine Exit-Funktion auf. Ich könnte den Exit-Code jedes Befehls überprüfen, aber ich frage mich, ob es einen Modus gibt, den ich aktivieren kann, oder eine Möglichkeit, Bash dazu zu bringen, dies automatisch zu tun.

Nehmen Sie zum Beispiel die folgenden Befehle:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


Gibt es eine Möglichkeit, wie ich der Shell vor dem Ausführen dieser Befehle irgendwie signalisieren kann, dass sie myfunc aufrufen soll, wenn einer von ihnen fehlschlägt, damit ich stattdessen etwas saubereres schreiben kann wie:

cd foo
rm a
cd bar
rm b

Antworten:


12

Sie können bash trap ERR verwenden , um Ihr Skript zu beenden, wenn ein Befehl einen Status größer als Null zurückgibt, und Ihre Funktion beim Beenden ausführen.

Etwas wie:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Beachten Sie, dass der Bash-Trap ERR implizit set -o errexitoder set -enicht POSIX ist.

Und der ERRTrap wird nicht ausgeführt, wenn der fehlgeschlagene Befehl Teil der unmittelbar folgenden Befehlsliste untiloder des whileSchlüsselworts, Teil des Tests nach den ifoder den elifreservierten Wörtern, Teil eines in &&oder in der ||Liste ausgeführten Befehls ist oder wenn der Rückgabestatus des Befehls mit invertiert wird !.


1

Eine (vielleicht) einfachere Variante der akzeptierten Antwort:

  1. Verwenden Sie set -e diese Option , um den Fehler eines Befehls zum Abbrechen der Ausführung einer Liste zu verursachen.
  2. Listen Sie einfach Ihre Befehle auf.
  3. Verwenden Sie eine if- then- elseAnweisung, um Ihre Fehlerbehandlungsbefehle auszuführen. Dieses letzte Stück ist etwas knifflig. Beobachten:
setze -e
wenn
    cmd 1                         # zB cd foo
     cmd 2                         # zB rm a
     cmd 3                         # zB cd bar
     cmd 4                         # zB rm b
dann
    setze + e
    Befehle, die bei Erfolg ausgeführt werden sollen (falls vorhanden)
sonst
    setze + e
    myfunc
    andere Befehle, die bei einem Fehler auszuführen sind (falls vorhanden) 
fi

Der schwierige Teil ist, dass Sie Ihre Befehle in den ifTeil des if- then- einfügen else, nicht in den thenTeil oder den elseTeil. Denken Sie daran, dass die Syntax der ifAnweisung lautet

wenn  Liste ; dann  Liste ; [elif  list ; dann  Liste ; ] ... [sonst  Liste ; ] fi 
   ↑↑↑↑
Das set -eteilt der Shell mit, dass wenn ( ) fehlschlägt, es nicht weitergehen und ( ) ausführen soll , und so weiter auf der ganzen Linie. Wenn dies einem Befehl auf der äußersten Ebene eines Shell-Skripts passiert, wird die Shell beendet. Da es sich bei · · · jedoch um eine (zusammengesetzte) Liste nach a handelt , führt der Fehler eines dieser vier Befehle einfach zum Fehlschlagen der gesamten Liste, wodurch die Klausel ausgeführt wird. Wenn alle vier Befehle erfolgreich sind, wird die Klausel ausgeführt.cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

In beiden Fällen sollten Sie wahrscheinlich zuerst die eOption deaktivieren (deaktivieren), indem Sie dies tun set +e. Andernfalls wird das Skript möglicherweise aus dem Wasser gesprengt, wenn ein Befehl myfuncfehlschlägt.

set -ewird in der POSIX-Spezifikation angegeben und beschrieben .


0

Nehmen Sie Ihr Wort " Jeder Befehl hängt von jedem vorherigen Befehl ab. Wenn ein Befehl fehlschlägt, sollte das gesamte Skript fehlschlagen ", denke ich, dass Sie keine spezielle Funktion benötigen, um die Fehler zu behandeln.

Alles, was Sie brauchen, ist, Ihre Befehle mit &&Operator und ||Operator zu verketten, die genau das tun, was Sie geschrieben haben.

Zum Beispiel wird diese Kette unterbrochen und es wird "etwas ist schief gelaufen" ausgegeben, wenn einer der vorherigen Befehle unterbrochen wurde (Bash liest von links nach rechts).

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Echtes Beispiel (ich habe dir foo, Datei a, dir bar und Datei b nur für eine echte Demo erstellt):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

Und schließlich, wenn alle Befehle erfolgreich ausgeführt wurden (Exit-Code 0), geht das Skript einfach weiter:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

Es ist wichtig, sich daran zu erinnern, dass bei Verwendung von && der nächste Befehl ausgeführt wird, wenn der vorherige Befehl mit dem Code 0 beendet wurde, was für bash Erfolg bedeutet.

Wenn ein Befehl in der Kette schief geht, folgt der Befehl / script / was auch immer || wird durchgeführt.

Und nur zur Veranschaulichung: Wenn Sie abhängig von dem Befehl, der fehlerhaft ausgeführt wurde, unterschiedliche Aktionen ausführen müssen, können Sie dies auch mit einem klassischen Skript tun, indem Sie überwachen, dessen Wert $?den Exit-Code des genau vorherigen Befehls meldet (gibt Null zurück, wenn der Befehl erfolgreich ausgeführt wurde oder eine andere positive Zahl, wenn der Befehl fehlgeschlagen ist)

Beispiel:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Ausgabe:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Tipp: Sie können die Meldung "bash: cd: bbar: Keine solche Datei ..." durch Anwenden unterdrücken eval $comm 2>/dev/null

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.