Comment vérifier l'état de sortie à l'aide d'une instruction if

256

Je me demandais quelle serait la meilleure façon de vérifier l'état de sortie dans une instruction if afin de faire écho à une sortie spécifique.

Je pense que c'est

if [ $? -eq 1 ]
then
   echo "blah blah blah"
fi

Le problème que j'ai également, c'est que l'instruction exit se trouve avant l'instruction if simplement parce qu'elle doit avoir ce code de sortie. De plus, je sais que je fais quelque chose de mal car la sortie quitterait évidemment le programme.

deadcell4
la source
3
Plaese poste votre script complet (ou au moins une portée plus large). Sinon, cela semble bien.
RedX
5
Si vous devez utiliser le code de sortie d'une invocation de programme particulière à deux endroits différents, vous devez le conserver - quelque chose commesome_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
twalberg

Réponses:

262

Chaque commande qui s'exécute a un statut de sortie.

Cette vérification examine l'état de sortie de la commande qui s'est terminée le plus récemment avant l'exécution de cette ligne.

Si vous voulez que votre script se termine lorsque ce test retourne vrai (la commande précédente a échoué), vous mettez exit 1(ou quoi que ce soit) à l'intérieur de ce ifbloc après leecho .

Cela étant dit, si vous exécutez la commande et que vous souhaitez tester sa sortie à l'aide de ce qui suit est souvent plus simple.

if some_command; then
    echo command returned true
else
    echo command returned some error
fi

Ou pour changer cela, utilisez !pour la négation

if ! some_command; then
    echo command returned some error
else
    echo command returned true
fi

Notez cependant qu'aucun de ces soins que le code d'erreur est. Si vous savez que vous ne vous souciez que d'un code d'erreur spécifique, vous devez vérifier $?manuellement.

Etan Reisner
la source
11
@ deadcell4 Quand on a besoin de terminer un script shell en cas d'échec d'un programme, l'idiome suivant est utilea_command || return 1
gboffi
10
@gboffi returnne fonctionne que dans une fonction et un script d'origine. Vous avez besoin exitde l'autre cas (qui en fait trop dans une fonction et un script source). Mais oui, c'est certainement un modèle raisonnable si vous n'avez pas besoin de nettoyage spécifique ou de sortie supplémentaire.
Etan Reisner
1
Je dois dire que dash, le shell non interactif par défaut dans de nombreuses distributions Linux modernes, ne se soucie pas de la distinction entre returnet à l' exitintérieur des scripts shell exécutés. dashquitte le script même si je l'utilise return.
gboffi
Quelle est la logique derrière les deux derniers contrôles? Il semble contre-intuitif que la condition if <command>passe si le code de sortie est 0. Dans toute autre langue, ce serait l'inverse
sjw
3
REMARQUE IMPORTANTE: cela ne fonctionnera pas pour les tuyaux. if ! some_command | some_other_commandignorera le statut de some_command. Les deux solutions de contournement les plus courantes consistent à set -o pipefail(peut modifier les fonctionnalités dans d'autres parties de votre programme) ou à déplacer l' ifinstruction en if [[ ${PIPESTATUS[0]} -ne 0 ]]tant que commande de suivi distincte (laide mais fonctionnelle). Si vous utilisez, set -evous souhaiterez également ajouter || trueà la fin du tuyau lors de l'utilisation de la deuxième solution, car la suppression du tuyau du flux de contrôle proposé par ifentraînerait sinon sa fermeture immédiate.
Alex Jansen
186

Notez que les codes de sortie! = 0 sont utilisés pour signaler une erreur. Donc, il vaut mieux faire:

retVal=$?
if [ $retVal -ne 0 ]; then
    echo "Error"
fi
exit $retVal

au lieu de

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal
Oo.oO
la source
Vous devez tester sur retVal, car $? après l'affectation de retVal n'est pas la valeur de retour de votre commande.
anr78
Pas vraiment: mywiki.wooledge.org/BashFAQ/002 - cependant, je suis d'accord que l'édition améliore la lisibilité.
Oo.oO
1
Je viens de trouver ce post qui l'explique stackoverflow.com/questions/20157938/…
anr78
dnf check-updaterenvoie 0 (aucune mise à jour), 100 (mises à jour disponibles) ou 1 (erreur).
jww
1
@jww - eh bien, ce n'est pas tout à fait une bonne idée d'aller à l'encontre de la convention ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Mais bon, rien n’empêche cela. Si les dnfdéveloppeurs ont choisi cette voie, c'est leur choix. Mais encore, leur choix ne fait pas casser les spécifications :)
Oo.oO
44

$?est un paramètre comme les autres. Vous pouvez enregistrer sa valeur à utiliser avant de finalement appeler exit.

exit_status=$?
if [ $exit_status -eq 1 ]; then
    echo "blah blah blah"
fi
exit $exit_status
chepner
la source
29

Alternative à la ifdéclaration explicite

Au minimum:

test $? -eq 0 || echo "something bad happened"

Achevée:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE
Catskul
la source
13

Juste pour ajouter à la réponse utile et détaillée :

Si vous devez vérifier explicitement le code de sortie, il est préférable d'utiliser l'opérateur arithmétique (( ... )), de cette façon:

run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }

Ou utilisez une casedéclaration:

run_some_command; ec=$?  # grab the exit code into a variable so that it can
                         # be reused later, without the fear of being overwritten
case $ec in
    0) ;;
    1) printf '%s\n' "Command exited with non-zero"; exit 1;;
    *) do_something_else;;
esac

Réponse connexe sur la gestion des erreurs dans Bash:

codeforester
la source