Comment arrêter le script bash lorsqu'une condition échoue?

11

Ici, il montre comment utiliser le ||et &&dans une seule ligne pour concaténer l'exécution des commandes: Comment puis-je vérifier les erreurs apt-get dans un script bash?

J'essaie d'arrêter l'exécution d'un script si une certaine condition échoue,

par exemple

false || echo "Obvious error because its false on left" && exit

Ici, il imprime Obvious error because its false on the leftet quitte la console qui est ce que je voulais.

true || echo "This shouldn't print" && exit

Ici, il n'y a pas d'impression d'écho mais la exitcommande s'exécute également, c'est-à-dire que la console est fermée, la exitcommande ne devrait-elle pas s'exécuter car la commande d'écho à droite n'a pas été exécutée? Ou est-ce par défaut une déclaration considérée comme fausse du côté gauche d'un &&opérateur?

Edit: j'aurais dû le mentionner avant, mon objectif était de faire écho à l'erreur et de sortir si ce n'était pas clair. par rapport à mon cas spécifique de capture d'erreurs lors du regroupement des conditions à l'aide de && et ||, la réponse @ bodhi.zazen résout le problème.

La réponse @takatakatek clarifie le contrôle de flux et les liens du guide bash sont excellents

@muru answer a une bonne explication de pourquoi ne pas utiliser set -e si vous voulez que des messages d'erreur personnalisés soient lancés avec des alternatives d'utilisation de perl et trap qui, je pense, est un moyen plus robuste et me vois l'utiliser à partir de mon deuxième script bash!

kosol
la source
Autant que je sache, il se termine lorsque le script est terminé.
Panther

Réponses:

10

Vous voulez probablement (comme l'a souligné Steeldriver)

true || { echo "This shouldn't print" && exit; }

Sinon, votre script lit un conditionnel à la fois

a ou b, puis quittez

Je pense que vous voulez un ou (b et sortir)?

Panthère
la source
4
En fait, vous devez utiliser { ... ; }(comme l'a suggéré @steeldriver), sinon la exitseule sortie du sous-shell, et le shell parent continue d'exécuter le script.
Gordon Davisson
14

Le problème est que || et && ne saute qu'une strophe suivante d'une chaîne de commandes lorsque la condition échoue. Si vous écrivez une structure de blocs complète, cela est parfaitement logique. Ce que vous avez écrit devient ceci:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

Ce que vous voulez c'est:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

Lorsque vous utilisez les opérateurs conditionnels de raccourci de Bash, il est préférable d'éviter de les mélanger. La logique est beaucoup plus facile à comprendre pendant que vous l'écrivez, et vos lecteurs apprécieront votre bon style.

takatakatek
la source
2
Oui, à mon avis, c'est beaucoup plus comment le conditionnement en bash devrait être écrit
derHugo
@takatakatek Cela rend plus clair pourquoi cela échoue dans mon cas. Je suis d'accord que la logique est claire dans celui-ci mais n'est pas la raison du regroupement des conditions en utilisant && et || est d'éviter de les exprimer en utilisant des instructions conditionnelles?
kosol
@kosol Oui, && et || combinez deux choses: tester l'état de sortie de la commande précédente et spécifier une seule commande (ou groupe de commandes) à exécuter (pour &&) ou à ignorer (pour ||). Ils peuvent être utiles pour des cas très simples, mais ils ne peuvent pas remplacer des if ... then ... elif ... else ... fiblocs entièrement exprimés . Je soupçonne que vous ne savez peut-être pas que Bash n'a pas de vrais types booléens vus dans des langages plus sophistiqués. Si l'état de sortie d'une commande est 0 (zéro), Bash le traite comme vrai / succès . Si l'état de sortie est différent de zéro, Bash le traite comme faux / échec .
takatakatek
7

Le moyen le plus simple d'échouer en cas d'erreur est d'utiliser l' -eoption de bash ( set -eou /bin/bash -edans le shebang). Cependant, cela ne facilite pas l'envoi de messages d'erreur personnalisés. Il y a quelques idiomes qui pourraient le rendre plus simple:

die à la Perl

Perl a une diecommande très pratique qui imprime un message à stderr et déclenche une exception, ce qui entraîne généralement la fin du script. Vous pouvez émuler ceci:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

La trap <command>commande peut être utilisée pour exécuter une commande lorsqu'un signal est reçu, ou lorsque vous quittez le script ( trap ... EXIT), ou lorsqu'une commande renvoie une erreur ( trap ... ERR). Donc quelque chose comme:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Puis:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

Dans les deux cas, je suggère d'utiliser au moins exit 1pour indiquer que le script a échoué . Une plaine exitutilisera le statut de sortie de la commande précédente, qui dans ces cas serait les commandes echoor printf, qui réussiraient généralement. Enregistrer l'état de sortie de la commande défaillante comme je l'ai fait ici serait une bonne chose.

muru
la source
6

Vous pouvez essayer de tester un peu le démarrage du script avec

#!/bin/bash -e

Le -e garantit que le script se termine au moment où quelque chose retourne faux. Une défaillance spécifique peut alors être évitée avecsomething || true

agrégat1166877
la source