Je veux que mes scripts shell échouent chaque fois qu'une commande exécutée avec eux échoue.
En général, je le fais avec:
set -e
set -o pipefail
(généralement j'ajoute set -u
aussi)
Le fait est que rien de ce qui précède ne fonctionne avec la substitution de processus. Ce code affiche "ok" et quitte avec le code retour = 0, alors que j'aimerais qu'il échoue:
#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)
Y a-t-il quelque chose d'équivalent à "pipefail" sauf pour la substitution de processus? Est-il possible de transmettre à une commande la sortie des commandes telles qu'elles étaient des fichiers, mais déclencher une erreur chaque fois que l'un de ces programmes échoue?
La solution d'un pauvre serait de détecter si ces commandes écrivent sur stderr (mais certaines commandes écrivent sur stderr dans des scénarios réussis).
Une autre solution plus conforme à Posix consisterait à utiliser des canaux nommés, mais je dois lancer ces commandes qui utilisent la substitution de processus comme des oneliners construits à la volée à partir du code compilé, et la création de canaux nommés compliquerait les choses (commandes supplémentaires, erreur de piégeage pour les supprimer, etc.)
la source
mkfifo pipe; { rm pipe; cat <file; } >pipe
. Cette commande va se bloquer jusqu'à ce qu'un lecteur s'ouvrepipe
car c'est le shell qui fait leopen()
et donc dès qu'il y a un lecteur surpipe
le lien fs pourpipe
isrm
'd puiscat
copie les fichiers dans le descripteur du shell pour ce pipe. Et de toute façon, si vous voulez propager une erreur à partir d'un sous-processus: <( ! : || kill -2 "$$")
$$
substitution ne fonctionne pas pour moi, car cette substitution de commande ne se fait pas car la commande qui utilise la substitution de processus se fait à l'intérieur d'un pipeline de commandes généré à partir d'un code "non shell" (python). Je devrais probablement créer un sous-processus en python et les diriger par programme.kill -2 0
.Réponses:
Vous ne pouvez contourner ce problème qu'avec cela, par exemple:
Le sous-shell du script est
SIGTERM
d avant que la deuxième commande puisse être exécutée (other_command
). Laecho ok
commande est exécutée "parfois": le problème est que les substitutions de processus sont asynchrones. Il n'y a aucune garantie que lakill $$
commande est exécutée avant ou après laecho ok
commande. C'est une question de planification des systèmes d'exploitation.Considérez un script bash comme celui-ci:
La sortie de ce script peut être:
Ou:
Vous pouvez l'essayer et après quelques essais, vous verrez les deux ordres différents dans la sortie. Dans le premier, le script a été interrompu avant que les deux autres
echo
commandes puissent écrire dans le descripteur de fichier. Dans le second, lafalse
ou lakill
commande était probablement programmée après lesecho
commandes.Ou pour être plus précis: l'appel système
signal()
de l'kill
utillité qui envoie leSIGTERM
signal au processus shell a été planifié (ou a été délivré) plus tard ou plus tôt que leswrite()
appels système d' écho .Mais cependant, le script s'arrête et le code de sortie n'est pas 0. Il devrait donc résoudre votre problème.
Une autre solution consiste, bien sûr, à utiliser des canaux nommés pour cela. Mais, cela dépend de la complexité de votre script pour implémenter des canaux nommés ou de la solution de contournement ci-dessus.
Les références:
la source
Pour mémoire, et même si les réponses et commentaires étaient bons et utiles, j'ai fini par implémenter quelque chose d'un peu différent (j'avais des restrictions sur la réception des signaux dans le processus parent que je n'ai pas mentionné dans la question)
En gros, j'ai fini par faire quelque chose comme ça:
Ensuite, je vérifie le fichier d'erreur. S'il existe, je sais quelle sous-commande a échoué (et le contenu du fichier d'erreur peut être utile). Plus verbeux et hack que je voulais à l'origine, mais moins lourd que de créer des pipes nommées dans une commande bash à une ligne.
la source
Cet exemple montre comment utiliser
kill
avectrap
.Mais
kill
ne peut pas passer un code retour de votre sous-processus au processus parent.la source
De la même manière que vous implémenteriez
$PIPESTATUS
/$pipestatus
avec un shell POSIX qui ne le prend pas en charge , vous pouvez obtenir l'état de sortie des commandes en les passant via un tube:Qui donne:
Ou vous pouvez utiliser
pipefail
et implémenter la substitution de processus à la main comme vous le feriez avec des shells qui ne le prennent pas en charge:la source