Si vous ne voulez vraiment pas que la deuxième commande soit exécutée tant que la première n'a pas réussi, vous devez probablement utiliser des fichiers temporaires. La version simple est la suivante :
tmp=${TMPDIR:-/tmp}/mine.$$
if ./a > $tmp.1
then
if ./b <$tmp.1 >$tmp.2
then
if ./c <$tmp.2
then : OK
else echo "./c failed" 1>&2
fi
else echo "./b failed" 1>&2
fi
else echo "./a failed" 1>&2
fi
rm -f $tmp.[12]
La redirection '1>&2' peut également être abrégée en '>&2' ; cependant, une ancienne version du shell MKS ne gérait pas correctement la redirection des erreurs sans le '1' précédent. J'utilise donc depuis longtemps cette notation sans ambiguïté pour la fiabilité.
Cela fait fuir les fichiers si vous interrompez quelque chose. Utilisations de la programmation shell à l'épreuve des bombes (plus ou moins) :
tmp=${TMPDIR:-/tmp}/mine.$$
trap 'rm -f $tmp.[12]; exit 1' 0 1 2 3 13 15
...if statement as before...
rm -f $tmp.[12]
trap 0 1 2 3 13 15
La première ligne piège dit "exécuter les commandes". rm -f $tmp.[12]; exit 1
' lorsque l'un des signaux 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE ou 15 SIGTERM se produit, ou 0 (lorsque l'interpréteur de commandes quitte pour une raison quelconque). Si vous écrivez un shell script, le piège final n'a besoin que de supprimer le piège sur 0, qui est le piège de sortie du shell (vous pouvez laisser les autres signaux en place puisque le processus est sur le point de se terminer de toute façon).
Dans le pipeline original, il est possible que 'c' lise les données de 'b' avant que 'a' n'ait terminé - ce qui est généralement souhaitable (cela donne du travail à plusieurs cœurs, par exemple). Si 'b' est une phase de 'tri', cela ne s'applique pas - 'b' doit voir toutes ses entrées avant de pouvoir générer ses sorties.
Si vous voulez détecter quelle(s) commande(s) échoue(nt), vous pouvez utiliser :
(./a || echo "./a exited with $?" 1>&2) |
(./b || echo "./b exited with $?" 1>&2) |
(./c || echo "./c exited with $?" 1>&2)
C'est simple et symétrique - il est trivial de l'étendre à un pipeline à 4 ou N parties.
Une simple expérience avec 'set -e' n'a rien donné.
8 votes
Il faudrait vraiment quelque chose comme
&&|
ce qui signifierait "ne continuer le pipe que si la commande précédente a réussi". Je suppose que vous pourriez aussi avoir|||
ce qui signifierait "continuer le pipe si la commande précédente a échoué" (et éventuellement transmettre le message d'erreur comme la commande de Bash 4|&
).7 votes
@DennisWilliamson, vous ne pouvez pas "arrêter le tuyau" parce que
a
,b
,c
Les commandes ne sont pas exécutées séquentiellement mais en parallèle. En d'autres termes, les données circulent séquentiellement dea
ac
mais l'actuela
,b
yc
Les commandes commencent (à peu près) au même moment.