it-swarm.it

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 Shell se uno dei comandi esce con un codice di uscita diverso da zero?

364
Mark Roddy

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.

469
paxdiablo

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.

213
Jeff Hill

"set -e" è probabilmente il modo più semplice per farlo. Mettilo solo prima di qualsiasi comando nel tuo programma.

50
Allen

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 $ ?.

28
Arvodan
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
25
chemila

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 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 .

21

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;
}
19

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
11
Martin W
#
#------------------------------------------------------------------------------
# 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
4
Yordan Georgiev