web-dev-qa-db-fra.com

Script shell de sortie basé sur le code de sortie de processus

J'ai un script shell qui exécute un certain nombre de commandes. Comment faire en sorte que le script shell se termine si l'une des commandes se termine avec un code de sortie non nul?

368
Mark Roddy

Après chaque commande, le code de sortie se trouve dans la variable $? afin que vous obteniez quelque chose comme:

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

Vous devez faire attention aux commandes piped car le $? ne vous donne que le code de retour du dernier élément du tube donc, dans le code:

ls -al file.ext | sed 's/^/xx: /"

ne renverra pas de code d'erreur si le fichier n'existe pas (puisque la partie sed du pipeline fonctionne, renvoyant 0).

Le shell bash fournit en réalité un tableau qui peut aider dans ce cas, c’est-à-dire PIPESTATUS. Ce tableau contient un élément pour chacun des composants du pipeline, auquel vous pouvez accéder individuellement, comme ${PIPESTATUS[0]}:

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

Notez que vous obtenez le résultat de la commande false, et non le pipeline entier. Vous pouvez également obtenir la liste complète à traiter comme bon vous semble:

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

Si vous voulez obtenir le code d'erreur le plus volumineux d'un pipeline, vous pouvez utiliser l'une des solutions suivantes:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

Cela parcourt chacun des éléments PIPESTATUS à son tour, en le stockant dans rc s'il était supérieur à la valeur précédente rc.

476
paxdiablo

Si vous voulez travailler avec $ ?, vous devrez le vérifier après chaque commande, puisque $? est mis à jour après chaque commande. Cela signifie que si vous exécutez un pipeline, vous n'obtiendrez que le code de sortie du dernier processus du pipeline.

Une autre approche consiste à faire ceci:

set -e
set -o pipefail

Si vous mettez ceci en haut du script Shell, il semble que bash s'en occupe pour vous. Comme l'a noté une affiche précédente, "set -e" entraînera la fermeture de bash avec une erreur sur une commande simple. "set -o pipefail" provoquera également la fermeture de bash avec une erreur sur toute commande d'un pipeline.

Voir ici ou ici pour un peu plus de discussion sur ce problème. Ici est la section manuelle de bash sur l'ensemble intégré.

214
Jeff Hill

"set -e" est probablement le moyen le plus simple de procéder. Il suffit de mettre cela avant toute commande dans votre programme.

51
Allen

Si vous appelez simplement exit dans le bash sans paramètre, le code de sortie de la dernière commande sera renvoyé. Combinée à OR, la bash ne doit appeler exit que si la commande précédente échoue. Mais je n'ai pas testé cela.

 command1 || exit; 
 command2 || sortie;

Le Bash stockera également le code de sortie de la dernière commande dans la variable $ ?.

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

http://cfaj.freeshell.org/Shell/cus-faq-2.html#11

  1. Comment obtenir le code de sortie de cmd1 dans cmd1|cmd2

    Tout d'abord, notez que le code de sortie cmd1 peut être différent de zéro et ne signifie toujours pas une erreur. Cela se produit par exemple dans

    cmd | head -1
    

    vous pouvez observer un état de sortie 141 (ou 269 avec ksh93) de cmd1, mais c'est parce que cmd a été interrompu par un signal SIGPIPE lorsque head -1 s'est arrêté après avoir lu une ligne.

    Connaître le statut de sortie des éléments d'un pipeline cmd1 | cmd2 | cmd3

    une. avec zsh:

    Les codes de sortie sont fournis dans le tableau spécial pipestatus. cmd1 le code de sortie est dans $pipestatus[1], cmd3 code de sortie dans $pipestatus[3], de sorte que $? est toujours identique à $pipestatus[-1].

    b. avec bash:

    Les codes de sortie sont fournis dans le tableau spécial PIPESTATUS. cmd1 le code de sortie est dans ${PIPESTATUS[0]}, cmd3 code de sortie dans ${PIPESTATUS[2]}, de sorte que $? est toujours identique à ${PIPESTATUS: -1}.

    ...

    Pour plus de détails, voir ce qui suit lien .

21
Vladimir Graschenko

pour 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

En bash c'est facile, il suffit de les lier avec &&:

command1 && command2 && command3

Vous pouvez également utiliser la construction if imbriquée:

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="$@" ;

    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