Ho uno script di shell che esegue un numero di comandi. Come faccio ad uscire dallo script Shell se uno dei comandi esce con un codice di uscita diverso da zero?
Dopo ogni comando, il codice di uscita può essere trovato nella variabile $?
in modo da avere qualcosa come:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
È necessario fare attenzione ai comandi piped poiché $?
fornisce solo il codice di ritorno dell'ultimo elemento nella pipe, quindi nel codice:
ls -al file.ext | sed 's/^/xx: /"
non restituirà un codice di errore se il file non esiste (poiché la parte sed
della pipeline funziona effettivamente, restituendo 0).
La shell bash
fornisce in realtà un array che può essere d'aiuto in quel caso, che è PIPESTATUS
. Questo array ha un elemento per ciascuno dei componenti della pipeline, a cui è possibile accedere singolarmente come ${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]}
1
Nota che questo ti sta dando il risultato del comando false
, non dell'intera pipeline. Puoi anche ottenere l'intero elenco da elaborare come meglio credi:
pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1
Se si desidera ottenere il codice di errore più grande da una pipeline, è possibile utilizzare qualcosa come:
true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc
Questo passa attraverso ciascuno degli elementi PIPESTATUS
a sua volta, memorizzandolo in rc
se era maggiore del precedente valore rc
.
Se vuoi lavorare con $ ?, dovrai controllarlo dopo ogni comando, da $? viene aggiornato dopo l'uscita di ciascun comando. Ciò significa che se si esegue una pipeline, si otterrà solo il codice di uscita dell'ultimo processo nella pipeline.
Un altro approccio è quello di fare questo:
set -e
set -o pipefail
Se lo metti all'inizio dello script di Shell, sembra che bash si prenderà cura di questo per te. Come notato in un poster precedente, "set -e" causerà l'uscita di bash con un errore su qualsiasi comando semplice. "set -o pipefail" causerà l'uscita di bash con un errore su qualsiasi comando in una pipeline.
Vedi qui o qui per un po 'più di discussione su questo problema. Qui è la sezione manuale di bash sul set incorporato.
"set -e
" è probabilmente il modo più semplice per farlo. Mettilo solo prima di qualsiasi comando nel tuo programma.
Se si chiama exit in bash senza parametri, verrà restituito il codice di uscita dell'ultimo comando. Combinato con OR la bash dovrebbe invocare exit solo se il precedente comando fallisce. Ma non ho provato questo.
comando1 || uscita; comando2 || Uscita;
Il Bash memorizzerà anche il codice di uscita dell'ultimo comando nella variabile $ ?.
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
http://cfaj.freeshell.org/Shell/cus-faq-2.html#11
Come ottengo il codice di uscita di cmd1
in cmd1|cmd2
Innanzitutto, nota che il codice di uscita cmd1
potrebbe essere diverso da zero e comunque non significa un errore. Questo accade ad esempio in
cmd | head -1
si potrebbe osservare uno stato di uscita 141 (o 269 con ksh93) di cmd1
, ma è perché cmd
è stato interrotto da un segnale SIGPIPE quando head -1
è terminato dopo aver letto una riga.
Conoscere lo stato di uscita degli elementi di una pipeline cmd1 | cmd2 | cmd3
un. con zsh:
I codici di uscita sono forniti nell'array speciale pipestatus. cmd1
il codice di uscita è in $pipestatus[1]
, cmd3
codice di uscita in $pipestatus[3]
, in modo che $?
sia sempre lo stesso di $pipestatus[-1]
.
b. con bash:
I codici di uscita sono forniti nell'array speciale PIPESTATUS
. cmd1
il codice di uscita è in ${PIPESTATUS[0]}
, cmd3
codice di uscita in ${PIPESTATUS[2]}
, in modo che $?
sia sempre lo stesso di ${PIPESTATUS: -1}
.
...
Per maggiori dettagli vedi il seguente link .
per bash:
# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;
#
# ... the rest of the script goes here
#
function catch_errors() {
# do whatever on errors
#
#
echo "script aborted, because of errors";
exit 0;
}
In bash è facile, basta legarli insieme a &&:
command1 && command2 && command3
Puoi anche usare il costrutto nidificato if:
if command1
then
if command2
then
do_something
else
exit
fi
else
exit
fi
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="[email protected]" ;
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?
# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"
if [ $exit_code -ne 0 ] ; then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code" "$error_msg"
else
#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"
fi
}
#eof func doRunCmdOrExit