Script shell de sortie basé sur le code de sortie de processus
Question
J'ai un script shell qui exécute un certain nombre de commandes. Comment faire en sorte que le script shell se ferme si l’une des commandes se termine avec un code de sortie non nul?
La solution
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 (car la partie sed
du pipeline fonctionne réellement et renvoie 0).
Le shell bash
fournit en fait un tableau qui peut être utile dans ce cas, sous la forme PIPESTATUS
. Ce tableau comporte 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 souhaitez obtenir le code d'erreur le plus volumineux à partir d'un pipeline, vous pouvez utiliser quelque chose du type:
true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc
Ceci parcourt chacun des éléments PIPESTATUS
, en le stockant dans rc
s'il était supérieur à la valeur rc
précédente. <
Autres conseils
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’occupe de cela pour vous. Comme l'a noté une affiche précédente, " set -e " provoquera la fermeture de bash avec une erreur sur une commande simple. "set -o pipefail" bash se ferme avec une erreur sur n'importe quelle commande d'un pipeline.
Voir ici ou ici pour un peu plus de discussion sur ce problème. Voici la section du manuel bash sur le plateau construit.
& définir -e
" est probablement le moyen le plus simple de le faire. 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 l'exit que si la commande précédente échoue. Mais je n'ai pas testé cela.
command1 || exit; command2 || exit;
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
danscmd1 | cmd2
Tout d'abord, notez que le code de sortie
cmd1
peut être différent de zéro et qu'il n'en est toujours pas. signifie une erreur. Cela se produit par exemple danscmd | head -1
vous pouvez observer un état de sortie 141 (ou 269 avec ksh93) de
cmd1
, mais c'est parce quecmd
a été interrompu par un signal SIGPIPE lorsquehead -1
s'est terminé après avoir lu une ligne.Pour connaître l'état de sortie des éléments d'un pipeline
cmd1 | cmd2 | cmd3
a. avec zsh:
Les codes de sortie sont fournis dans le tableau spécial pipestatus. Le code de sortie
cmd1
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
. Le code de sortiecmd1
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 le lien suivant.
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;
}
Pour cela, rien de plus simple, associez-les simplement avec & amp; & amp;:
.command1 && command2 && command3
Vous pouvez également utiliser la construction imbriquée de 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