2008-12-15 11 views
28

Angenommen, ein Shell-Skript (/ bin/sh oder/bin/bash) enthielt mehrere Befehle. Wie kann ich das Skript sauber beenden, wenn einer der Befehle einen fehlgeschlagenen Beendigungsstatus hat? Natürlich kann man Blöcke und/oder Rückrufe verwenden, aber gibt es eine sauberere, prägnantere Art und Weise? Die Verwendung von & & ist auch nicht wirklich eine Option, weil die Befehle lang sein können, oder das Skript könnte nicht-triviale Dinge wie Schleifen und Bedingungen haben.Shell Scripting: sterben auf jeden Fehler

Antwort

52

Mit Standard sh und bash können Sie

set -e 

Es wird

$ help set 
... 
     -e Exit immediately if a command exits with a non-zero status. 

Es funktioniert auch (von dem, was ich sammeln konnte) mit zsh. Es sollte auch für alle Bourne Shell-Nachkommen funktionieren.

Mit csh/tcsh, haben Sie Ihr Skript starten mit #!/bin/csh -e

+0

Danke, das zu sein, was scheint, ich will. Ich sollte meine Google Fu schärfen, ich denke ... :) – Pistos

+3

Beachten Sie, dass Befehle in Conditionals fehlschlagen können, ohne das Skript zu beenden - was entscheidend ist. Zum Beispiel: wenn grep etwas/etwas/wo; dann: es wurde gefunden; sonst: es wurde nicht gefunden; Es funktioniert gut, egal ob etwas in/etwas/wo gefunden wird. –

+0

Sie sagen "Standard-sh". Bedeutet das, dass es POSIX ist? Edit: Ich habe es nachgeschlagen und es ist POSIX: http://pubs.opengroup.org/onlinepubs/009695399/utilities/set.html – Taywee

16

werden können, die Sie verwenden können:

$ <any_command> || exit 1 
+1

Dieses Muster ist nützlich für ** selektiv beenden bei Ausfall einzelner Befehle ** und auch **, wenn Sie zuerst eine benutzerdefinierte Fehlermeldung ** drucken möchten. Um letzteres zu tun, verwenden Sie etwas wie ' || eval 'echo "Bitte sicherstellen ..." 1> & 2; exit 1'' HINWEIS: Der 'eval'-Trick ist notwendig, um die 'echo'- und' exit'-Befehle zusammen zu gruppieren, ohne eine _sub_shell zu erzeugen; Die Verwendung einer Untershell (wie es bei Verwendung von Klammern der Fall wäre) würde den "Exit" unwirksam machen. – mklement0

+14

Live und lernen: Bash verfügt über eine Befehlsgruppierung, so dass der von mir erwähnte 'eval'-Trick nicht benötigt wird. Verwenden Sie stattdessen ' || {echo "Bitte stellen Sie sicher ..." 1> & 2; Ausfahrt; } '(notieren Sie die erforderlichen'; 'vor dem Schließen'} '). – mklement0

0

Sie können $ überprüfen? zu sehen, was der jüngste Exit-Code ist ..

beispiel

#!/bin/sh 
# A Tidier approach 

check_errs() 
{ 
    # Function. Parameter 1 is the return code 
    # Para. 2 is text to display on failure. 
    if [ "${1}" -ne "0" ]; then 
    echo "ERROR # ${1} : ${2}" 
    # as a bonus, make our script exit with the right error code. 
    exit ${1} 
    fi 
} 

### main script starts here ### 

grep "^${1}:" /etc/passwd > /dev/null 2>&1 
check_errs $? "User ${1} not found in /etc/passwd" 
USERNAME=`grep "^${1}:" /etc/passwd|cut -d":" -f1` 
check_errs $? "Cut returned an error" 
echo "USERNAME: $USERNAME" 
check_errs $? "echo returned an error - very strange!" 
+1

-1: Dies ist ein allzu häufiges Antipattern. @ Barun Lösung ist einfacher und idiomatischer. – tripleee

+0

das gleiche Skript wie oben, aber als ein sauberer "aufgeräumter Ansatz" (alle in einer Zeile): foo = $ (grep "^ $ {1}"/etc/passwd) && foo = $ (echo "$ foo" | cut -d: -f1) && echo ok || echo not_ok (NB: wenn bash verwendet wird, und nicht beschränkt auf sh, kann man den Exit-Status eines beliebigen Teils einer Pipeline erhalten, was die grep/cut-Logik vereinfacht.) – michael

+0

verwenden Sie einfach -e – Codefor