385 votes

Quitter le shell script en fonction du code de sortie du processus

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

0 votes

Méthode dure : tester la valeur de $? après chaque commande. Méthode simple : mettre set -e o #!/bin/bash -e en haut de votre script Bash.

502voto

paxdiablo Points 341644

Après chaque commande, le code de sortie peut être trouvé dans le fichier $? de sorte que vous auriez quelque chose comme :

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

Vous devez être prudent avec les commandes pipées, car l'option $? ne vous donne que le code de retour du dernier élément du tuyau 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 l'option sed la partie du pipeline fonctionne réellement, en retournant 0).

El bash Le shell fournit en fait un tableau qui peut aider dans ce cas, à savoir PIPESTATUS . Ce tableau comporte un élément pour chacun des composants du pipeline, auquel vous pouvez accéder individuellement comme suit ${PIPESTATUS[0]} :

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

Notez que cela vous permet d'obtenir le résultat de la fonction false mais pas le pipeline entier. Vous pouvez également obtenir la liste entière pour la traiter comme bon vous semble :

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

Si vous vouliez obtenir le plus grand code d'erreur d'un pipeline, vous pourriez utiliser quelque chose comme :

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

Cela passe par chacun des PIPESTATUS à son tour, en le stockant dans rc si elle était supérieure à la précédente rc valeur.

39 votes

La même fonctionnalité en une seule ligne de code portable : ls -al file.ext || exit $? ( [[ ]] n'est pas portable )

19 votes

MarcH, je pense que vous trouverez que [[ ]] est assez portable dans bash ce qui est le but de la question :-) C'est assez étrange, ls ne fonctionne pas dans command.com donc ce n'est pas portable non plus, spécieux je sais, mais c'est le même genre d'argument que vous présentez.

39 votes

Je sais que c'est ancien, mais il faut noter que vous pouvez obtenir le code de sortie des commandes dans un pipe via le tableau PIPESTATUS (c'est-à-dire, ${PIPESTATUS[0]} pour la première commande, ${PIPESTATUS[1]} pour le second, ou ${PIPESTATUS[*]} pour une liste de tous les états de sortie.

225voto

Jeff Hill Points 22158

Si vous voulez travailler avec $? vous devrez le vérifier après chaque commande, car $? est mis à jour après la sortie de 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 shell script, il semble que Bash s'en chargera pour vous. Comme un poster précédent l'a noté, "set -e" fera sortir Bash avec une erreur sur n'importe quelle commande simple. L'option "set -o pipefail" permet à Bash de quitter avec une erreur pour toute commande dans un pipeline.

Voir aquí o aquí pour une discussion plus approfondie sur ce problème. Ici est la section du manuel Bash sur le set builtin.

6 votes

Cela devrait être la première réponse : il est beaucoup, beaucoup plus facile de faire cela que d'utiliser PIPESTATUS et vérifier les codes de sortie partout.

2 votes

#!/bin/bash -e est la seule façon de démarrer un script. Vous pouvez toujours utiliser des choses comme foo || handle_error $? si vous devez réellement examiner les statuts de sortie.

0 votes

@DavisHerring C'est incorrect ou trompeur à plusieurs égards. L'obus -e a quelques cas de figure surprenants, et son utilisation est donc même déconseillée dans certaines directives, en particulier pour les débutants. Et le fait de la spécifier sur la ligne shebang est fragile ; mettre set -e dans le script lui-même est plus robuste.

54voto

Allen Points 3497

" set -e "est probablement le moyen le plus simple de le faire. Il suffit de le placer avant toute commande dans votre programme.

6 votes

@SwaroopCH set -e votre script s'interrompra si une commande de votre script sort avec un statut d'erreur et que vous n'avez pas géré cette erreur.

2 votes

set -e est équivalent à 100% à set -o errexit qui, contrairement au premier, peut faire l'objet d'une recherche. Recherchez opengroup + errexit pour la documentation officielle.

31voto

Arvodan Points 450

Si vous appelez simplement exit dans Bash sans aucun paramètre, il renverra le code de sortie de la dernière commande. Combiné avec OR Bash ne devrait invoquer exit que si la commande précédente échoue. Mais je n'ai pas testé cela.

command1 || exit;
command2 || exit;

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

26voto

chemila Points 704
[ $? -eq 0 ] || exit $?; # Exit for nonzero return code

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X