Le langage exact utilisé dans la spécification Single UNIX pour décrire la signification deset -e
est:
Lorsque cette option est activée, si une commande simple échoue pour l'une des raisons répertoriées dans Conséquences des erreurs de shell ou renvoie une valeur de statut de sortie> 0, et n'est pas une [commande conditionnelle ou inversée], le shell doit immédiatement quitter.
Il y a une ambiguïté sur ce qui se passe lorsqu'une telle commande est exécutée dans un sous - shell . D'un point de vue pratique, tout ce que le sous-shell peut faire est de quitter et de renvoyer un statut différent de zéro au shell parent. La sortie du shell parent dépend de la transformation de cet état non nul en une simple commande échouant dans le shell parent.
Un de ces problèmes est celui que vous avez rencontré: un état de retour différent de zéro à partir d'une substitution de commande . Étant donné que cet état est ignoré, le shell parent ne se ferme pas. Comme vous l'avez déjà découvert , un moyen de prendre en compte le statut de sortie consiste à utiliser la substitution de commande dans une affectation simple : le statut de sortie de l'affectation est alors le statut de sortie de la dernière substitution de commande dans la ou les assignations .
Notez que cela fonctionnera comme prévu uniquement s'il y a une substitution de commande unique, car seul le statut de la dernière substitution est pris en compte. Par exemple, la commande suivante réussit (à la fois selon le standard et dans chaque implémentation que j'ai vue):
a=$(false)$(echo foo)
Un autre cas à surveiller est sous - couches explicites : (somecommand)
. Selon l'interprétation ci-dessus, le sous-shell peut renvoyer un statut différent de zéro, mais comme il ne s'agit pas d'une simple commande dans le shell parent, le shell parent doit continuer. En fait, tous les obus que je connais font revenir le parent à ce stade. Bien que cela soit utile dans de nombreux cas, par exemple (cd /some/dir && somecommand)
lorsque les parenthèses sont utilisées pour conserver une opération telle qu'un changement d'annuaire en cours, il enfreint la spécification si elle set -e
est désactivée dans le sous-shell ou si le sous-shell renvoie un statut différent de zéro de manière à ce que ne le terminerait pas, par exemple en utilisant !
une vraie commande. Par exemple, ash, bash, pdksh, ksh93 et zsh se ferment sans afficher foo
les exemples suivants:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Pourtant, aucune commande simple n'a échoué tant qu'elle set -e
était en vigueur!
Un troisième cas problématique concerne les éléments d'un pipeline non trivial . En pratique, tous les shells ignorent les défaillances des éléments du pipeline autres que le dernier et présentent l'un des deux comportements suivants en ce qui concerne le dernier élément de pipeline:
- ATT ksh et zsh, qui exécutent le dernier élément du pipeline dans le shell parent, fonctionnent comme d'habitude: si une commande simple échoue dans le dernier élément du pipeline, le shell qui l'exécute, qui se trouve être le shell parent, sorties.
- D'autres shells approchent le comportement en quittant si le dernier élément du pipeline renvoie un statut différent de zéro.
Comme auparavant, le fait de désactiver set -e
ou d'utiliser une négation dans le dernier élément du pipeline entraîne le renvoi d'un statut différent de zéro de manière à ne pas terminer le shell; les obus autres que ATT ksh et zsh vont alors sortir.
L' pipefail
option de Bash entraîne la fermeture immédiate d'un pipeline set -e
si l'un de ses éléments renvoie un statut différent de zéro.
Notez que, complication supplémentaire, bash est désactivé set -e
dans les sous-shell, sauf s’il est en mode POSIX ( set -o posix
ou POSIXLY_CORRECT
dans l’environnement au démarrage de bash).
Tout cela montre que la spécification POSIX fait malheureusement un travail médiocre en spécifiant l' -e
option. Heureusement, les obus existants sont généralement cohérents dans leur comportement.