it-swarm.com.de

Automatisches Beenden des Bash-Shell-Skripts bei Fehlern

Ich habe ein Shell-Skript geschrieben, und ich würde es nützlich finden, wenn die Ausführung dieses Shell-Skripts angehalten werden könnte, wenn einer der Befehle fehlschlägt. Ein Beispiel finden Sie unten:

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install

Wenn das Skript in diesem Fall nicht in das angegebene Verzeichnis wechseln kann, möchte es im Falle eines Fehlschlags mit Sicherheit keine ./configure nachträglich ausführen.

Jetzt ist mir klar, dass ich für jeden Befehl eine if-Prüfung durchführen kann (was meiner Meinung nach eine hoffnungslose Lösung ist). Gibt es jedoch eine globale Einstellung, mit der das Skript beendet werden kann, wenn einer der Befehle fehlschlägt?

545
radman

Verwenden Sie den integrierten set -e :

_#!/bin/bash
set -e
# Any subsequent(*) commands which fail will cause the Shell script to exit immediately
_

Alternativ können Sie _-e_ in der Befehlszeile übergeben:

_bash -e my_script.sh
_

Sie können disable dieses Verhalten auch mit _set +e_ ausführen.

Möglicherweise möchten Sie auch alle oder einige der Optionen _-e-u-x_ und _-o pipefail_ wie folgt verwenden:

_set -euxo pipefail
_

_-e_ wird bei Fehlern beendet, _-u_ bei undefinierten Variablen und -o (for option) pipefail wird bei Fehlern in der Befehls-Pipe beendet. Einige Fallstricke und Problemumgehungen sind gut dokumentiert hier .

(*) Hinweis:

Die Shell beendet nicht , wenn der fehlgeschlagene Befehl Teil der Befehlsliste ist, die unmittelbar auf einen while oder ) folgt + bis Schlüsselwort, Teil des Tests nach if oder Elif reservierten Wörtern, Teil eines beliebigen Befehls wird in einer && oder || Liste ausgeführt, mit Ausnahme des Befehls nach dem final && oder || , jeder Befehl in einer Pipeline außer dem letzten oder wenn der Rückgabewert des Befehls mit ! invertiert wird

(von _man bash_)

850
Adam Rosenfield

Um das Skript zu beenden, sobald einer der Befehle fehlgeschlagen ist, fügen Sie dies am Anfang hinzu:

set -e

Dadurch wird das Skript sofort beendet, wenn ein Befehl, der nicht Teil eines Tests ist (z. B. in einer if [ ... ]-Bedingung oder einem && -Konstrukt), mit einem Exit-Code ungleich Null beendet wird.

63
sth

So geht's:

#!/bin/sh

abort()
{
    echo >&2 '
***************
*** ABORTED ***
***************
'
    echo "An error occurred. Exiting..." >&2
    exit 1
}

trap 'abort' 0

set -e

# Add your script below....
# If an error occurs, the abort() function will be called.
#----------------------------------------------------------
# ===> Your script goes here
# Done!
trap : 0

echo >&2 '
************
*** DONE *** 
************
'
48
supercobra

Verwenden Sie es in Verbindung mit pipefail.

set -e
set -o pipefail

-e (errexit): Abbruch des Skripts beim ersten Fehler, wenn ein Befehl mit einem Status ungleich Null beendet wird (außer in till oder while-Schleifen, if-Tests, Listenkonstrukten)

-o Pipefail: Bewirkt, dass eine Pipeline den Beendigungsstatus des letzten Befehls in der Pipeline zurückgibt, der einen Rückgabewert ungleich Null zurückgegeben hat.

Kapitel 33. Optionen

34

Eine Alternative zur akzeptierten Antwort, die in die erste Zeile passt:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install
22
Petr Peller

Eine Redewendung ist:

cd some_dir && ./configure --some-flags && make && make install

Mir ist klar, dass es lange dauern kann, aber für größere Skripte könnte man es in logische Funktionen aufteilen.

19

Ich denke, was Sie suchen, ist der Befehl trap:

trap command signal [signal ...]

Weitere Informationen finden Sie unter diese Seite .

Eine andere Option ist die Verwendung des Befehls set -e am oberen Rand Ihres Skripts - das Skript wird beendet, wenn ein Programm/Befehl einen nicht wahren Wert zurückgibt.

17
a_m0d

Ein Punkt, der in den vorhandenen Antworten übersehen wird, ist das Erben der Fehlerfallen. Die bash-Shell bietet eine solche Option für die Verwendung von set

-E

Wenn diese Option aktiviert ist, werden alle Traps in ERR von Shell-Funktionen, Befehlsersetzungen und Befehlen übernommen, die in einer Subshell-Umgebung ausgeführt werden. Die ERR-Falle wird normalerweise nicht in solchen Fällen vererbt.


Adam Rosenfields Antwort Die Empfehlung, _set -e_ zu verwenden, ist in bestimmten Fällen richtig, hat aber auch seine eigenen potenziellen Gefahren. Siehe GreyCat's BashFAQ - 105 - Warum setzt -e (oder setzt -o errexit oder trap ERR) nicht das, was ich erwartet hatte?

Set -e wird laut Handbuch beendet

wenn ein einfacher Befehl mit einem Status ungleich Null beendet wird. Die Shell wird nicht beendet, wenn der fehlgeschlagene Befehl Teil der Befehlsliste ist, die unmittelbar auf ein while oder until Schlüsselwort folgt, das Teil des _test in a if statement_ ist., Teil einer && oder || Liste mit Ausnahme des Befehls nach final && or || , any command in a pipeline but the last, oder wenn der Rückgabewert des Befehls über ! "invertiert wird.

was bedeutet, dass _set -e_ in den folgenden einfachen Fällen nicht funktioniert (detaillierte Erklärungen finden Sie im Wiki)

  1. Verwenden Sie den arithmetischen Operator let oder $((..)) (bash ab 4.1), um einen Variablenwert zu inkrementieren

    _#!/usr/bin/env bash
    set -e
    i=0
    let i++                   # or ((i++)) on bash 4.1 or later
    echo "i is $i" 
    _
  2. Wenn der fehlerhafte Befehl nicht Teil des letzten Befehls ist, der über _&&_ oder _||_ ausgeführt wurde. Für z.B. Die unten stehende Falle würde nicht feuern, wenn dies erwartet wird

    _#!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    _
  3. Bei falscher Verwendung in einer if-Anweisung als ist der Exit-Code der if-Anweisung der Exit-Code des zuletzt ausgeführten Befehls. Im folgenden Beispiel war der zuletzt ausgeführte Befehl echo, der die Falle nicht auslösen würde, obwohl _test -d_ fehlgeschlagen ist

    _#!/usr/bin/env bash
    set -e
    f() { if test -d nosuchdir; then echo no dir; fi; }
    f 
    echo survived
    _
  4. Bei Verwendung mit Befehlsersetzung werden sie ignoriert, es sei denn, _inherit_errexit_ wurde mit bash 4.4 festgelegt

    _#!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    _
  5. wenn Sie Befehle verwenden, die wie Zuweisungen aussehen, dies jedoch nicht sind, z. B. export, declare, typeset oder local. Hier wird der Funktionsaufruf von f not beendet, da local den zuvor eingestellten Fehlercode überstrichen hat.

    _set -e
    f() { local var=$(somecommand that fails); }        
    g() { local var; var=$(somecommand that fails); }
    _
  6. Bei Verwendung in einer Pipeline ist der fehlerhafte Befehl nicht Teil des letzten Befehls. Für z.B. Der folgende Befehl würde noch ausgeführt werden. Eine Möglichkeit besteht darin, pipefail zu aktivieren, indem Sie den Exit-Code des fehlgeschlagenen Prozesses first zurückgeben:

    _set -e
    somecommand that fails | cat -
    echo survived
    _

Die ideale Empfehlung ist, nicht _set -e_ zu verwenden und stattdessen eine eigene Version der Fehlerprüfung zu implementieren. Weitere Informationen zum Implementieren der benutzerdefinierten Fehlerbehandlung in einer meiner Antworten auf Fehler in einem Bash-Skript auslösen

0
Inian