2009-05-04 6 views
306

Ho uno script Bash shell che richiama un numero di comandi. Mi piacerebbe che lo script della shell esca automaticamente con un valore di ritorno di 1 se uno qualsiasi dei comandi restituisce un valore diverso da zero.Annullare uno script di shell se qualche comando restituisce un valore diverso da zero?

Ciò è possibile senza controllare esplicitamente il risultato di ciascun comando?

ad es.

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

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

In aggiunta a 'set -e', fare anche' set -u' (o 'set -eu'). '-u' mette fine al comportamento idiota, che nasconde i bug che è possibile accedere a qualsiasi variabile inesistente e avere un valore vuoto prodotto senza diagnostica. – Kaz

risposta

550

Aggiungere questo all'inizio dello script:

set -e 

Questo farà sì che la shell per uscire immediatamente se un semplice comando termina con un valore diverso da zero. Un comando semplice è qualsiasi comando non incluso in un test if, while o until, o parte di un & & o || elenco.

Vedere il bash(1) man page sul comando interno "set" per ulteriori dettagli.

Io personalmente avvio quasi tutti gli script di shell con "set -e". È davvero fastidioso che una sceneggiatura continui ostinatamente quando qualcosa non va nel mezzo e rompe le ipotesi per il resto della sceneggiatura.

+32

Funzionerebbe, ma mi piace usare "#!/Usr/bin/env bash" perché eseguo spesso 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. –

+2

Ricorda che questo potrebbe non essere sufficiente a causa delle condotte. Vedi la mia risposta aggiunta. – leonbloy

+45

Inoltre, i flag sulla linea shebang vengono ignorati se uno script viene eseguito come 'bash script.sh'. –

3

Un'espressione come

dosomething1 && dosomething2 && dosomething3 

interrompe l'elaborazione quando uno dei comandi restituisce un valore diverso da zero. Ad esempio, il seguente comando non sarà mai la stampa "fatto":

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

Run con -e o set -e in alto.

Vedere anche set -u.

+26

Per potenzialmente salvare gli altri è necessario leggere 'help set':' -u' tratta i riferimenti alle variabili non impostate come errori. – mklement0

+1

quindi è "set -u' o" set -e', non entrambi? @ lumpynose – ericn

+1

@eric Sono andato in pensione diversi anni fa. Anche se ho amato il mio lavoro, il mio vecchio cervello ha dimenticato tutto. A mano a mano, immagino che potresti usarli entrambi insieme; brutta formulazione da parte mia; Avrei dovuto dire "e/o". – lumpynose

64

Le istruzioni if ​​nell'esempio non sono necessarie. Basta fare in questo modo:

dosomething1 || exit 1 

Se si prende il consiglio di Ville Laurikari e utilizzare set -e poi per alcuni comandi potrebbe essere necessario utilizzare questo:

dosomething || true 

Il || true farà il comando pipeline di avere un true restituisce il valore anche se il comando non riesce, quindi l'opzione -e non ucciderà lo script.

+1

Mi piace. Soprattutto perché la risposta principale è bash-centrica (non mi è chiaro se/fino a che punto si applica allo scripting zsh). E potrei cercarlo, ma la tua è solo più chiara, perché logica. – g33kz0r

23

Se è necessario eseguire la pulizia all'uscita, è anche possibile utilizzare "trap" con l'ERR dello pseudo-segnale. Funziona allo stesso modo di intrappolare INT o qualsiasi altro segnale; bash getta ERR se tutte le uscite di comando 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, soprattutto se si sta utilizzando "set -e", si potrebbe intrappolare EXIT; la trappola verrà quindi eseguita quando lo script termina per qualsiasi motivo, inclusa una fine normale, interruzioni, un'uscita causata dall'opzione -e, ecc.

8

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

I casi in cui è richiesta $? è quando deve essere controllato contro valori multipli:

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

o quando $? necessità di essere riutilizzati o altrimenti manipolati:

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

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

+0

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

138

aggiungere al risposta accettata:

Tenere a mente che set -e a volte non è sufficiente, specialmente se si dispone di pip es.

Ad esempio, si supponga di avere questo script

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

... che funziona come ci si aspetta: un errore nella configure interrompe l'esecuzione.

Domani si apporta una modifica apparentemente banale:

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

... e ora non funziona. Questo si spiega here, e una soluzione alternativa (Bash solo) è previsto:

 
#!/bin/bash 
set -e 
set -o pipefail 

./configure | tee configure.log 
make 
-2

appena gettare in un altro uno per riferimento in quanto non vi era una domanda aggiuntiva di inserire Mark Edgars ed ecco un esempio aggiuntivo e tocca sul tema nel complesso:

[[ `cmd` ]] && echo success_else_silence 

, che è la stessa di cmd || exit errcode come qualcuno ha mostrato.

es. Voglio fare in modo una partizione viene smontato in caso di montaggio:

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

No, '[[' 'cmd']]' non è la stessa cosa. È falso se l'output del comando è vuoto e vero altrimenti, indipendentemente dallo stato di uscita del comando. – Gilles

2
#!/bin/bash -e 

dovrebbe essere sufficiente.