Annullare uno script di shell se un comando restituisce un valore diverso da zero?


437

Ho uno script di shell Bash che invoca un numero di comandi. Vorrei che lo script della shell uscisse automaticamente con un valore di ritorno di 1 se uno qualsiasi dei comandi restituisce un valore diverso da zero.

È possibile senza controllare esplicitamente il risultato di ciascun comando?

per esempio

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi

8
Inoltre set -e, anche do set -u(o set -eu). -upone fine al comportamento idiota e nascosto che consente di accedere a qualsiasi variabile inesistente e di produrre un valore vuoto senza diagnostica.
Kaz,

Risposte:


742

Aggiungi questo all'inizio dello script:

set -e

Ciò causerà la chiusura immediata della shell se un semplice comando esce con un valore di uscita diverso da zero. Un comando semplice è qualsiasi comando che non fa parte di un if, while, o until test, o parte di un && o || elenco.

Vedi la pagina man bash (1) sul comando interno "set" per maggiori dettagli.

Personalmente inizio quasi tutti gli script di shell con "set -e". È davvero fastidioso che una sceneggiatura continui ostinatamente quando qualcosa non riesce nel mezzo e infrange le ipotesi per il resto della sceneggiatura.


36
Funzionerebbe, ma mi piace usare "#! / Usr / bin / env bash" perché eseguo frequentemente bash da un posto diverso da / bin. E "#! / Usr / bin / env bash -e" non funziona. Inoltre, è bello avere un posto da modificare per leggere "set -xe" quando voglio attivare la traccia per il debug.
Ville Laurikari,

48
Inoltre, i flag sulla linea shebang vengono ignorati se uno script viene eseguito come bash script.sh.
Tom Anderson,

27
Solo una nota: se si dichiarano le funzioni all'interno dello script bash, per estendere questa funzionalità è necessario che le funzioni -e siano state ridisegnate nel corpo della funzione.
Jin Kim,

8
Inoltre, se provi il tuo script, la riga shebang sarà irrilevante.

4
@JinKim Questo non sembra essere il caso in bash 3.2.48. Provate il seguente all'interno di uno script: set -e; tf() { false; }; tf; echo 'still here'. Anche senza set -eall'interno del corpo di tf(), l'esecuzione viene interrotta. Forse intendevi dire che set -enon è ereditato dai subshells , il che è vero.
mklement0

202

Per aggiungere alla risposta accettata:

Tieni presente che a set -evolte non è abbastanza, specialmente se hai le pipe.

Ad esempio, supponiamo di avere questo script

#!/bin/bash
set -e 
./configure  > configure.log
make

... che funziona come previsto: un errore configureinterrompe l'esecuzione.

Domani appari un cambiamento apparentemente banale:

#!/bin/bash
set -e 
./configure  | tee configure.log
make

... e ora non funziona. Questo è spiegato qui , e viene fornita una soluzione alternativa (solo Bash):

#! / Bin / bash
set -e 
imposta -o pipefail

./configure | tee configure.log
rendere

1
Grazie per aver spiegato l'importanza di dover pipefailandare d'accordo set -o!
Malcolm,

83

Le istruzioni if ​​nel tuo esempio non sono necessarie. Fallo così:

dosomething1 || exit 1

Se segui il consiglio di Ville Laurikari e lo usi, set -eper alcuni comandi potresti dover usare questo:

dosomething || true

La || truefarà il comando gasdotto hanno un truevalore di ritorno, anche se il comando non riesce in modo che il l' -eopzione non ucciderà lo script.


1
Mi piace questo. Soprattutto perché la risposta migliore è bash-centrica (non mi è affatto chiaro se / in che misura si applica allo scripting zsh). E potrei cercarlo, ma il tuo è solo più chiaro, perché la logica.
g33kz0r

set -enon è bash-centrico - è supportato anche sulla Bourne Shell originale.
Marcos Vives Del Sol,

27

Se hai una pulizia che devi fare all'uscita, puoi anche usare 'trap' con lo pseudo-segnale ERR. Funziona allo stesso modo del trapping INT o di qualsiasi altro segnale; bash genera ERR se un comando esce con un valore diverso da zero:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah

Oppure, specialmente se stai usando "set -e", potresti intercettare EXIT; la tua trap verrà quindi eseguita quando lo script esce per qualsiasi motivo, tra cui una fine normale, interruzioni, un'uscita causata dall'opzione -e, ecc.


12

La $?variabile è raramente necessaria. Lo pseudo-idioma command; if [ $? -eq 0 ]; then X; fidovrebbe sempre essere scritto come if command; then X; fi.

I casi in cui $?è richiesto è quando deve essere verificato su più valori:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac

o quando $?deve essere riutilizzato o altrimenti manipolato:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi

3
Perché "dovrebbe sempre essere scritto come"? Voglio dire, perché " dovrebbe " essere così? Quando un comando è lungo (pensa a invocare GCC con una dozzina di opzioni), allora è molto più leggibile eseguire il comando prima di controllare lo stato di ritorno.
ysap

Se un comando è troppo lungo, è possibile suddividerlo nominandolo (definire una funzione shell).
Mark Edgar,

12

Eseguilo con -eo set -ein alto.

Guarda anche set -u.


34
Per salvare potenzialmente gli altri la necessità di leggere help set: -utratta i riferimenti a variabili non impostate come errori.
mklement0

1
quindi è o set -uo set -enon entrambi? @lumpynose
ericn,

1
@eric Mi sono ritirato diversi anni fa. Anche se ho amato il mio lavoro, il mio cervello invecchiato ha dimenticato tutto. Direttamente immagino che potresti usarli entrambi insieme; parolacce da parte mia; Avrei dovuto dire "e / o".
Lumpynose,

3

Un'espressione simile

dosomething1 && dosomething2 && dosomething3

interromperà l'elaborazione quando uno dei comandi ritorna con un valore diverso da zero. Ad esempio, il comando seguente non stamperà mai "completato":

cat nosuchfile && echo "done"
echo $?
1


-2

aggiungendone un altro come riferimento, poiché c'era un'altra domanda sull'input di Mark Edgars e qui c'è un esempio aggiuntivo e tocca l'argomento in generale:

[[ `cmd` ]] && echo success_else_silence

che è uguale a cmd || exit errcodequello mostrato da qualcuno.

per esempio. Voglio assicurarmi che una partizione sia smontata se montata:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 

5
No, [[ cmd`]] `non è la stessa cosa. È falso se l'output del comando è vuoto e vero altrimenti, indipendentemente dallo stato di uscita del comando.
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.