Esci dallo script della shell in base al codice di uscita del processo
Domanda
Ho uno script di shell che esegue una serie di comandi.Come faccio a far uscire lo script della shell se uno qualsiasi dei comandi esce con un codice di uscita diverso da zero?
Soluzione
Dopo ogni comando, il codice di uscita può essere trovato nel file $?
variabile quindi avresti qualcosa del tipo:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
È necessario fare attenzione ai comandi trasmessi poiché il file $?
ti dà 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é il file sed
parte della pipeline funziona effettivamente, restituendo 0).
IL bash
shell in realtà fornisce un array che può aiutare in quel caso, cioè PIPESTATUS
.Questo array ha un elemento per ciascuno dei componenti della pipeline, a cui puoi accedere individualmente ${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]}
1
Nota che questo ti sta dando il risultato di false
comando, non l'intera pipeline.Puoi anche far elaborare l'intero elenco come ritieni opportuno:
pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1
Se volessi ottenere il codice di errore più grande da una pipeline, potresti usare 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 dei PIPESTATUS
elementi a loro volta, memorizzandoli rc
se fosse maggiore del precedente rc
valore.
Altri suggerimenti
Se vuoi lavorare con $?, dovrai controllarlo dopo ogni comando, poiché $?viene aggiornato dopo l'uscita di ogni comando.Ciò significa che se esegui una pipeline, otterrai 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 in cima allo script della shell, sembra che bash se ne occuperà per te.Come notato in precedenza, "set -e" farà sì che bash esca con un errore su qualsiasi comando semplice."set -o pipefail" farà sì che bash esca con un errore anche su qualsiasi comando in una pipeline.
Vedere Qui O Qui per una discussione più approfondita su questo problema. Qui è la sezione manuale di bash sul set integrato.
"set -e
" è probabilmente il modo più semplice per farlo.Mettilo prima di qualsiasi comando nel tuo programma.
Se chiami semplicemente exit nella bash senza parametri, restituirà il codice di uscita dell'ultimo comando.Combinato con OR la bash dovrebbe invocare exit solo se il comando precedente fallisce.Ma non l'ho testato.
command1 || exit; command2 || exit;
La 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 posso ottenere il codice di uscita di
cmd1
Incmd1|cmd2
Innanzitutto, notalo
cmd1
Il codice di uscita potrebbe essere diverso da zero e ancora non significare un errore.Ciò accade ad esempio incmd | head -1
potresti osservare uno stato di uscita 141 (o 269 con ksh93).
cmd1
, ma è perchécmd
è stato interrotto da un segnale SIGPIPE quandohead -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 è inserito$pipestatus[1]
,cmd3
codice di uscita in$pipestatus[3]
, affinché$?
è sempre lo stesso di$pipestatus[-1]
.B.con bash:
I codici di uscita sono forniti nel file
PIPESTATUS
matrice speciale.cmd1
il codice di uscita è inserito${PIPESTATUS[0]}
,cmd3
codice di uscita in${PIPESTATUS[2]}
, affinché$?
è sempre lo stesso di${PIPESTATUS: -1}
....
Per maggiori dettagli vedere quanto segue collegamento.
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 collegarli insieme con &&:
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="$@" ;
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