Comment attendre dans un script bash que plusieurs sous-processus générés à partir de ce script se terminent et retournent le code de sortie! = 0 lorsqu'un des sous-processus se termine par le code! = 0?
Script simple:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
Le script ci-dessus attendra les 10 sous-processus générés, mais il donnera toujours le statut de sortie 0 (voir help wait
). Comment puis-je modifier ce script pour qu'il découvre les états de sortie des sous-processus générés et renvoie le code de sortie 1 lorsque l'un des sous-processus se termine par le code! = 0?
Existe-t-il une meilleure solution pour cela que de collecter les PID des sous-processus, de les attendre dans l'ordre et de additionner les états de sortie?
wait -n
, disponible dans bash moderne pour revenir uniquement lorsque la première / prochaine commande est terminée.wait -n
a un petit problème: s'il ne reste aucun travail enfant (alias condition de concurrence ), il renvoie un état de sortie différent de zéro (échec) qui peut être distingué d'un processus enfant échoué.Réponses:
wait
prend (facultativement) le PID du processus à attendre, et avec $! vous obtenez le PID de la dernière commande lancée en arrière-plan. Modifiez la boucle pour stocker le PID de chaque sous-processus généré dans un tableau, puis bouclez à nouveau en attente sur chaque PID.la source
for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;
non?http://jeremy.zawodny.com/blog/archives/010717.html :
la source
jobs -p
donne des PID de sous-processus qui sont en état d'exécution. Il sautera un processus si le processus se termine avantjobs -p
son appel. Donc, si l'un des sous-processus se termine avantjobs -p
, l'état de sortie de ce processus sera perdu.jobs -p
ne donne pas de PID de sous-processus, mais plutôt des GPID . La logique d'attente semble fonctionner de toute façon, elle attend toujours le groupe si un tel groupe existe et pid sinon, mais il est bon d'être conscient .. surtout si l'on devait s'appuyer sur cela et incorporer quelque chose comme l'envoi de messages au sous-processus dans lequel cas, la syntaxe est différente selon que vous avez des PID ou des GPID .. iekill -- -$GPID
vskill $PID
Voici un exemple simple d'utilisation
wait
.Exécutez certains processus:
Attendez-les ensuite avec la
wait
commande:Ou tout simplement
wait
(sans arguments) pour tous.Cela attendra que tous les travaux en arrière-plan soient terminés.
Si l'
-n
option est fournie, attend la fin du travail suivant et retourne son état de sortie.Voir:
help wait
ethelp jobs
pour la syntaxe.Cependant, l'inconvénient est que cela ne retournera que le statut du dernier ID, vous devez donc vérifier le statut de chaque sous-processus et le stocker dans la variable.
Ou faites votre fonction de calcul pour créer un fichier en cas d'échec (vide ou avec journal des échecs), puis vérifiez ce fichier s'il existe, par exemple
la source
sleep 20 && true
etsleep 20 && false
- c'est-à-dire: remplacez ceux par votre (vos) fonction (s). Pour comprendre&&
et||
, exécutezman bash
et tapez '/' (recherche) puis '^ * Listes' (une expression régulière) puis entrez: l'homme&&
||
||
de capturer STDERR en échec également.wait
Si GNU Parallel est installé, vous pouvez faire:
GNU Parallel vous donnera le code de sortie:
0 - Toutes les tâches ont été exécutées sans erreur.
1-253 - Certains travaux ont échoué. L'état de sortie donne le nombre de travaux ayant échoué
254 - Plus de 253 travaux ont échoué.
255 - Autre erreur.
Regardez les vidéos d'introduction pour en savoir plus: http://pi.dk/1
la source
doCalculations
est une fonction définie dans ce même script (bien que l'OP n'était pas clair sur cette exigence). Quand j'essaye,parallel
dit/bin/bash: doCalculations: command not found
(il le dit 10 fois pour l'seq 0 9
exemple ci-dessus). Voir ici pour une solution de contournement.xargs
a une certaine capacité de lancer des travaux en parallèle via l'-P
option. De là :export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"
. Les limitations dexargs
sont énumérées dans la page de manuel deparallel
.doCalculations
s'appuie sur d'autres variables d'environnement internes au script (personnaliséesPATH
, etc.), elles doivent probablement être éditées explicitementexport
avant le lancementparallel
.wget -O - pi.dk/3 | sh
vous n'obtiendrez aucune confusion. Si votre emballeur a gâché les choses pour vous, je vous encourage à soulever le problème avec votre emballeur. Les variables et les fonctions doivent être exportées (export -f) pour GNU Parallel pour les voir (voirman parallel
: gnu.org/software/parallel/… )Que diriez-vous simplement:
Mise à jour:
Comme l'ont souligné plusieurs commentateurs, ce qui précède attend que tous les processus soient terminés avant de continuer, mais ne se termine pas et échoue si l'un d'entre eux échoue, cela peut être fait avec la modification suivante suggérée par @Bryan, @SamBrightman et d'autres :
la source
for pid in $pids; do wait $pid; done
help wait
- avec plusieurs IDwait
renvoie le code de sortie du dernier uniquement, comme l'a dit @ vlad-frolov ci-dessus.wait
appel du correspondant ? Il s'avère que ce n'est pas un problème: si vouswait
sur un processus qui a déjà été quitté, vous quitterezwait
immédiatement avec le statut du processus déjà terminé. (Merci,bash
auteurs!)Voici ce que j'ai trouvé jusqu'à présent. Je voudrais voir comment interrompre la commande de sommeil si un enfant se termine, afin que l'on n'ait pas à s'adapter
WAITALL_DELAY
à son utilisation.la source
Pour paralléliser cela ...
Traduisez-le en ceci ...
--max-procs
fonction de la quantité de parallélisme que vous souhaitez (0
signifie «tout à la fois»).xargs
- mais il n'est pas toujours installé par défaut.for
boucle n'est pas strictement nécessaire dans cet exemple car ilecho $i
s'agit simplement de régénérer la sortie de$(whatever_list
). Je pense simplement que l'utilisation dufor
mot - clé permet de voir un peu plus facilement ce qui se passe.Voici un exemple de travail simplifié ...
la source
--max-procs
: Comment obtenir le nombre de CPU / cœurs sous Linux à partir de la ligne de commande?Je ne pense pas que ce soit possible avec la fonctionnalité intégrée de Bash.
Vous pouvez recevoir une notification lorsqu'un enfant quitte:
Cependant, il n'y a aucun moyen apparent d'obtenir le statut de sortie de l'enfant dans le gestionnaire de signaux.
Obtenir ce statut enfant est généralement le travail de la
wait
famille de fonctions dans les API POSIX de niveau inférieur. Malheureusement, le support de Bash est limité - vous pouvez attendre un processus enfant spécifique (et obtenir son statut de sortie) ou vous pouvez tous les attendre , et toujours obtenir un résultat 0.Ce qu'il semble impossible de faire est l'équivalent de
waitpid(-1)
, qui bloque jusqu'à ce que tout processus enfant revienne.la source
Je vois beaucoup de bons exemples énumérés ici, je voulais aussi jeter le mien.
J'utilise quelque chose de très similaire pour démarrer / arrêter des serveurs / services en parallèle et vérifier chaque état de sortie. Fonctionne très bien pour moi. J'espère que cela aide quelqu'un!
la source
trap "kill 0" EXIT
C'est quelque chose que j'utilise:
la source
Le code suivant attend la fin de tous les calculs et renvoie l'état de sortie 1 en cas d' échec de doCalculations .
la source
Il suffit de stocker les résultats hors du shell, par exemple dans un fichier.
la source
Voici ma version qui fonctionne pour plusieurs pids, enregistre les avertissements si l'exécution prend trop de temps et arrête les sous-processus si l'exécution prend plus de temps qu'une valeur donnée.
Exemple, attendez la fin des trois processus, enregistrez un avertissement si l'exécution prend plus de 5 secondes, arrêtez tous les processus si l'exécution dure plus de 120 secondes. Ne quittez pas le programme en cas d'échecs.
la source
Si vous disposez de bash 4.2 ou version ultérieure, les éléments suivants peuvent vous être utiles. Il utilise des tableaux associatifs pour stocker les noms de tâches et leur "code" ainsi que les noms de tâches et leurs pids. J'ai également intégré une méthode de limitation de débit simple qui pourrait être utile si vos tâches consomment beaucoup de temps processeur ou d'E / S et que vous souhaitez limiter le nombre de tâches simultanées.
Le script lance toutes les tâches dans la première boucle et consomme les résultats dans la seconde.
C'est un peu exagéré pour les cas simples mais cela permet des trucs assez soignés. Par exemple, on peut stocker des messages d'erreur pour chaque tâche dans un autre tableau associatif et les imprimer une fois que tout est réglé.
la source
Je viens de modifier un script en arrière-plan et de paralléliser un processus.
J'ai fait quelques essais (sur Solaris avec bash et ksh) et j'ai découvert que `` wait '' sort l'état de sortie s'il n'est pas nul, ou une liste de travaux qui retournent une sortie non nulle quand aucun argument PID n'est fourni. Par exemple
Frapper:
Ksh:
Cette sortie est écrite dans stderr, donc une solution simple à l'exemple OP pourrait être:
Alors que ce:
renverra également un nombre, mais sans le fichier tmp. Cela peut également être utilisé de cette façon, par exemple:
Mais ce n'est pas beaucoup plus utile que le fichier tmp IMO. Je ne pouvais pas trouver un moyen utile d'éviter le fichier tmp tout en évitant d'exécuter l '"attente" dans un sous-shell, qui ne fonctionnera pas du tout.
la source
J'ai essayé et j'ai combiné toutes les meilleures parties des autres exemples ici. Ce script exécutera la
checkpids
fonction à la fin d' un processus d'arrière-plan et affichera l'état de sortie sans recourir à l'interrogation.la source
set -m
vous permet d'utiliser fg & bg dans un scriptfg
, en plus de mettre le dernier processus au premier plan, a le même statut de sortie que le processus qu'il met en avantwhile fg
arrêtera la boucle lorsque desfg
sorties avec un état de sortie non nulmalheureusement, cela ne traitera pas le cas lorsqu'un processus en arrière-plan se termine avec un état de sortie non nul. (la boucle ne se terminera pas immédiatement. elle attendra la fin des processus précédents.)
la source
Il y a déjà beaucoup de réponses ici, mais je suis surpris que personne ne semble avoir suggéré d'utiliser des tableaux ... Alors voici ce que j'ai fait - cela pourrait être utile à certains à l'avenir.
la source
Cela fonctionne, devrait être aussi bon sinon meilleur que la réponse de @ HoverHell!
et bien sûr, j'ai immortalisé ce script, dans un projet NPM qui vous permet d'exécuter des commandes bash en parallèle, utile pour tester:
https://github.com/ORESoftware/generic-subshell
la source
trap $? $$
semble mettre le code de sortie à 0 et le PID au shell bash en cours d'exécution, à chaque fois pour moipiège est votre ami. Vous pouvez intercepter ERR dans de nombreux systèmes. Vous pouvez intercepter EXIT ou DEBUG pour exécuter un morceau de code après chaque commande.
Ceci en plus de tous les signaux standard.
la source
En
set -e
haut, votre script s'arrête en cas d'échec.expect
retournera1
si un sous-job a échoué.la source
Exactement à cet effet, j'ai écrit une
bash
fonction appelée:for
.Remarque :
:for
non seulement conserve et renvoie le code de sortie de la fonction défaillante, mais met également fin à toutes les instances en cours d'exécution en parallèle. Ce qui pourrait ne pas être nécessaire dans ce cas.usage
for.sh
:Références
la source
Je l'ai utilisé récemment (grâce à Alnitak):
De là, on peut facilement extrapoler, et avoir un déclencheur (toucher un fichier, envoyer un signal) et changer les critères de comptage (compter les fichiers touchés, ou autre) pour répondre à ce déclencheur. Ou si vous voulez juste 'any' rc non nul, tuez simplement le verrou de save_status.
la source
J'en avais besoin, mais le processus cible n'était pas un enfant du shell actuel, auquel cas
wait $PID
cela ne fonctionne pas. J'ai trouvé l'alternative suivante à la place:Cela repose sur la présence de procfs , qui peuvent ne pas être disponibles (Mac ne le fournit pas par exemple). Donc, pour la portabilité, vous pouvez utiliser ceci à la place:
la source
Le piégeage du signal CHLD peut ne pas fonctionner car vous pouvez perdre certains signaux s'ils arrivent simultanément.
la source
solution d'attendre plusieurs sous-processus et de quitter lorsque l'un d'eux se termine avec un code d'état différent de zéro en utilisant 'wait -n'
le code d'état «127» est pour un processus inexistant, ce qui signifie que l'enfant peut être sorti.
la source
Attendez tous les travaux et renvoyez le code de sortie du dernier travail ayant échoué. Contrairement aux solutions ci-dessus, cela ne nécessite pas de sauvegarde pid. Juste bg loin, et attendez.
la source
Il peut y avoir un cas où le processus est terminé avant d'attendre le processus. Si nous déclenchons l'attente d'un processus qui est déjà terminé, cela déclenchera une erreur comme pid n'est pas un enfant de ce shell. Pour éviter de tels cas, la fonction suivante peut être utilisée pour déterminer si le processus est terminé ou non:
la source
Je pense que la façon la plus simple d'exécuter des travaux en parallèle et de vérifier l'état consiste à utiliser des fichiers temporaires. Il existe déjà quelques réponses similaires (par exemple Nietzche-jou et mug896).
Le code ci-dessus n'est pas sûr pour les threads. Si vous craignez que le code ci-dessus ne s'exécute en même temps que lui-même, il est préférable d'utiliser un nom de fichier plus unique, comme fail. $$. La dernière ligne doit répondre à l'exigence: "retourner le code de sortie 1 lorsque l'un des sous-processus se termine par le code! = 0?" J'ai jeté une exigence supplémentaire pour nettoyer. Il aurait peut-être été plus clair de l'écrire comme ceci:
Voici un extrait similaire pour rassembler les résultats de plusieurs travaux: je crée un répertoire temporaire, j'expose les sorties de toutes les sous-tâches dans un fichier séparé, puis je les vide pour examen. Cela ne correspond pas vraiment à la question - je le jette en bonus:
la source
Je suis presque tombé dans le piège de l'utilisation
jobs -p
pour collecter des PID, ce qui ne fonctionne pas si l'enfant est déjà sorti, comme indiqué dans le script ci-dessous. La solution que j'ai choisie consistait simplement à appelerwait -n
N fois, où N est le nombre d'enfants que j'ai, que je connais de façon déterministe.Production:
la source