Esci da Shell Script in base al codice di uscita del processo

Ho uno script di shell che esegue un numero di comandi. Come faccio ad uscire dallo script della shell se uno dei comandi esce con un codice di uscita diverso da zero?

Dopo ogni comando, il codice di uscita può essere trovato in $? variabile in modo da avere qualcosa come:

 ls -al file.ext rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi 

Devi fare attenzione ai comandi inviati dal $? solo ti dà il codice di ritorno dell’ultimo elemento nella pipe così, 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, ovvero PIPESTATUS . Questo array ha un elemento per ciascuno dei componenti della pipeline, che puoi accedere individualmente come ${PIPESTATUS[0]} :

 pax> false | true ; echo ${PIPESTATUS[0]} 1 

Nota che questo ti sta dando il risultato del false comando, 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, è ansible 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 di 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 della 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. Ecco la sezione manuale di bash sul set integrato.

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

  1. Come ottengo il codice di uscita di cmd1 in cmd1|cmd2

    Innanzitutto, nota che il codice di uscita di cmd1 potrebbe essere diverso da zero e non significa ancora 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.

    Per conoscere lo stato di uscita degli elementi di una pipeline cmd1 | cmd2 | cmd3 cmd1 | cmd2 | cmd3

    un. con zsh:

    I codici di uscita sono forniti nell’array speciale pipestatus. cmd1 codice di uscita di cmd1 è in $pipestatus[1] , il codice di uscita di cmd3 in $pipestatus[3] , quindi $? è sempre uguale a $pipestatus[-1] .

    b. con bash:

    I codici di uscita sono forniti nello speciale array PIPESTATUS . cmd1 codice di uscita di cmd1 è in ${PIPESTATUS[0]} , il codice di uscita di cmd3 in ${PIPESTATUS[2]} , quindi $? è sempre uguale a ${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