J'ai récemment rencontré des scripts comme celui-ci:
( set -e ; do-stuff; do-more-stuff; ) || echo failed
Cela me semble bien, mais cela ne fonctionne pas! Le set -e
ne s'applique pas lorsque vous ajoutez le ||
. Sans cela, cela fonctionne très bien:
$ ( set -e; false; echo passed; ); echo $?
1
Cependant, si j'ajoute le ||
, le set -e
est ignoré:
$ ( set -e; false; echo passed; ) || echo failed
passed
L'utilisation d'un vrai shell séparé fonctionne comme prévu:
$ sh -c 'set -e; false; echo passed;' || echo failed
failed
J'ai essayé cela dans plusieurs shells différents (bash, dash, ksh93) et tous se comportent de la même manière, donc ce n'est pas un bug. Quelqu'un peut-il expliquer cela?
shell
shell-script
Scientifique fou
la source
la source
||
extérieur du sous-shell affecte le comportement à l'intérieur du sous-shell.(set -e; echo 1; false; echo 2)
avec(set -e; echo 1; false; echo 2) || echo 3
Réponses:
Selon ce fil , c'est le comportement que POSIX spécifie pour utiliser "
set -e
" dans un sous-shell.(J'ai également été surpris.)
Tout d'abord, le comportement:
Le deuxième post note,
Il y a un peu plus dans le quatrième post, également par Eric Blake,
Ce comportement est certainement surprenant. C'est contre-intuitif: on s'attendrait à ce que la réactivation de
set -e
ait un effet, et que le contexte environnant ne prenne pas le pas; en outre, le libellé de la norme POSIX ne le rend pas particulièrement clair. Si vous la lisez dans le contexte où la commande échoue, la règle ne s'applique pas: elle ne s'applique que dans le contexte environnant, cependant, elle s'y applique complètement.la source
set -e; (false; echo passed;) || echo failed
. Cela ne m'étonne pas, en fait, que -e soit ignoré dans ce cas étant donné le libellé de la norme. Dans mon cas, cependant, je définis explicitement -e dans le sous-shell et je m'attends à ce que le sous-shell se termine en cas d'échec. Il n'y a pas de liste ET-OU dans le sous-shell ...if
et||
et&&
sont infectieux? c'est absurdeEn effet,
set -e
n'a aucun effet à l'intérieur des sous-coquilles si vous utilisez l'||
opérateur après eux; par exemple, cela ne fonctionnerait pas:Aaron D. Marasco dans sa réponse explique très bien pourquoi il se comporte de cette façon.
Voici une petite astuce qui peut être utilisée pour résoudre ce problème: exécutez la commande interne en arrière-plan, puis attendez-la immédiatement. La fonction
wait
intégrée renverra le code de sortie de la commande interne, et maintenant vous utilisez||
aprèswait
, pas la fonction interne, doncset -e
fonctionne correctement à l'intérieur de cette dernière:Voici la fonction générique qui s'appuie sur cette idée. Cela devrait fonctionner dans tous les shells compatibles POSIX si vous supprimez des
local
mots clés, c'est-à-dire remplacez toutlocal x=y
par justex=y
:Exemple d'utilisation:
Exécuter l'exemple:
La seule chose dont vous devez être conscient lorsque vous utilisez cette méthode est que toutes les modifications des variables Shell effectuées à partir de la commande à laquelle vous passez
run
ne se propageront pas à la fonction appelante, car la commande s'exécute dans un sous-shell.la source
Je n'exclurais pas que ce soit un bug simplement parce que plusieurs obus se comportent de cette façon. ;-)
J'ai plus de plaisir à offrir:
Puis-je citer man bash (4.2.24):
Peut-être que l'évaluation sur plusieurs commandes conduit à ignorer le || le contexte.
la source
eval
astuce ne fonctionne pas pour moi. J'ai essayé bash, bash en mode posix et dash.set -e
.set -e
est cassé par conception. Je ne l'utiliserais pas pour autre chose que le plus simple des scripts shell sans structures de contrôle, sous-shell ou substitutions de commandes.Solution de contournement lorsque usint toplevel
set -e
Je suis venu à cette question parce que j'utilisais
set -e
comme méthode de détection d'erreur:et sans
||
, le script cesserait de fonctionner et n'atteindrait jamaisdo_more_stuff
.Puisqu'il ne semble pas y avoir de solution propre, je pense que je vais simplement faire un simple
set +e
sur mes scripts:la source