Zamknij skrypt w przypadku błędu

141

Buduję skrypt powłoki, który ma taką iffunkcję:

if jarsigner -verbose -keystore $keyst -keystore $pass $jar_file $kalias
then
    echo $jar_file signed sucessfully
else
    echo ERROR: Failed to sign $jar_file. Please recheck the variables
fi

...

Chcę, aby wykonanie skryptu zakończyło się po wyświetleniu komunikatu o błędzie. Jak mogę to zrobić?

Nathan Campos
źródło

Odpowiedzi:

137

Szukasz exit?

To jest najlepszy przewodnik po bashu. http://tldp.org/LDP/abs/html/

W kontekście:

if jarsigner -verbose -keystore $keyst -keystore $pass $jar_file $kalias
then
    echo $jar_file signed sucessfully
else
    echo ERROR: Failed to sign $jar_file. Please recheck the variables 1>&2
    exit 1 # terminate and indicate error
fi

...
Byron Whitlock
źródło
5
Jeśli lubisz ABS, pokochasz BashGuide , BashFAQ i BashPitfalls .
Wstrzymano do odwołania.
Te linki Bash są NIESAMOWITE! BashFAQ byłby lepiej ustawiony jako BashRecipes.
Pete Alvin
337

Jeśli umieścisz set -eskrypt, skrypt zakończy się, gdy tylko jakiekolwiek polecenie w nim zawiedzie (tj. Gdy tylko polecenie zwróci niezerowy status). Nie pozwala to na napisanie własnej wiadomości, ale często wystarczają własne wiadomości polecenia, które się nie udało.

Zaletą tego podejścia jest to, że jest automatyczne: nie ryzykujesz, że zapomnisz zająć się przypadkiem błędu.

Polecenia, których stan jest testowany przez warunek (taki jak if, &&lub ||) nie przerywają skryptu (w przeciwnym razie warunek byłby bezcelowy). Idiomem do okazjonalnego polecenia, którego błąd nie ma znaczenia command-that-may-fail || true. Możesz także set -ewyłączyć część skryptu za pomocą set +e.

Gilles 'SO- przestań być zły'
źródło
3
Według mywiki.wooledge.org/BashFAQ/105 - ta funkcja ma historię niejasną i zawiłą w określaniu, które kody błędów poleceń powodują automatyczne zakończenie. Co więcej, „zasady zmieniają się z jednej wersji Bash na inną, ponieważ Bash próbuje śledzić skrajnie śliską definicję POSIX tej 'funkcji'”. Zgadzam się z @Dennis Williamson i zaakceptowaną odpowiedzią stackoverflow.com/questions/19622198/ ... - użyj pułapki 'error_handler' ERR. Nawet jeśli to pułapka!
Bondolin
3
często użycie -xflagi z -eflagą wystarczy do śledzenia miejsca, w którym program się kończy. Oznacza to, że użytkownik skryptu jest również programistą.
Alex,
Fajnie, ale myślę, że OP musiał wyjść ze skryptu z samodzielnie zdefiniowanego wyjątku.
vdegenne
42

Jeśli chcesz móc obsłużyć błąd zamiast ślepego zamykania, zamiast używać set -e, użyj a trapna ERRpseudo sygnale.

#!/bin/bash
f () {
    errorCode=$? # save the exit code as the first thing done in the trap function
    echo "error $errorCode"
    echo "the command executing at the time of the error was"
    echo "$BASH_COMMAND"
    echo "on line ${BASH_LINENO[0]}"
    # do some error handling, cleanup, logging, notification
    # $BASH_COMMAND contains the command that was being executed at the time of the trap
    # ${BASH_LINENO[0]} contains the line number in the script of that command
    # exit the script or return to try again, etc.
    exit $errorCode  # or use some other value or do return instead
}
trap f ERR
# do some stuff
false # returns 1 so it triggers the trap
# maybe do some other stuff

Inne pułapki można ustawić do obsługi innych sygnałów, w tym zwykłych sygnałów Unix oraz innych pseudo sygnałów Bash RETURNi DEBUG.

Wstrzymano do odwołania.
źródło
8

Oto sposób, aby to zrobić:

#!/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 *** 
************
'
supercobra
źródło
Dlaczego komunikat GOTOWE do stderr?
MattBianco,
1
Jest to powszechna praktyka, więc możesz przesłać dane wyjściowe skryptu do standardowego wyjścia, aby inny proces mógł je pobrać bez umieszczania komunikatów informacyjnych w środku.
supercobra
2
Prawdopodobnie równie powszechną praktyką jest traktowanie czegokolwiek na stderr jako wskaźnika problemów.
MattBianco
1
W przeszłości „set -e” zawsze działał dla mnie, ale dziś wieczorem natknąłem się na sytuację w obrazie Alpine Linux Docker, w której nie miało to żadnego skutku. To rozwiązanie zadziałało dla mnie i przywróciło mnie do wykonywanego zadania. Bardzo cenione.
synthesizerpatel
@supercobra Wspólna praktyka? Gdzie?
Thorbjørn Ravn Andersen
-8

exit 1to wszystko czego potrzebujesz. Jest 1to kod powrotu, więc możesz go zmienić, jeśli chcesz, powiedzmy, 1oznaczać udany przebieg i -1oznaczać awarię lub coś w tym rodzaju.

DGH
źródło
13
W systemie UNIX sukces zawsze wynosi 0. Może to pomóc w przypadku używania testlub &&lub ||.
mouviciel
5
Aby rozwinąć komentarz mouviciela: w skryptach powłoki 0 zawsze oznacza sukces, a od 1 do 255 oznacza porażkę. -1 jest poza zakresem (i często daje taki sam efekt jak 255, więc błąd taki jak 1).
Gilles 'SO- przestań być zły'
@mouviciel, @Gilles: Dzięki za dodatkowe informacje. Minęło trochę czasu, odkąd zajmowałem się bashem.
DGH,
To zły przykład użycia kodu powrotnego, w przeciwnym razie byłaby to świetna odpowiedź.
Brad Koch
1
Oprócz niepoprawnego zalecenia 1 dla sukcesu, -1 nie jest możliwe (kody wyjścia są bez znaku).
tripleee