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?
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
.
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é.
"set -e
" est probablement le moyen le plus simple de procéder. Il suffit de mettre cela avant toute commande dans votre programme.
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 $ ?.
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
http://cfaj.freeshell.org/Shell/cus-faq-2.html#11
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 .
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;
}
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
#
#------------------------------------------------------------------------------
# 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