Si un processus enfant généré échoue, tuez tout et quittez

9

Dans mon script, je divise un ensemble de données en input_aa, input_ab, etc. Ensuite, je les exécute à travers le même script Python, en tant que tel:

# Execute program on each split file
for part in input_*; do
        python3 $part &
done
wait

Ma question est double: comment puis-je détecter qu'un processus Python a échoué, et lorsqu'il est détecté, comment puis-je tuer tous les enfants générés et quitter le script avec un échec?

Arriver à
la source

Réponses:

10

Vous pouvez utiliser un groupe de processus:

set -m
(
   for part in input_*; do
     (python3 "$part" || kill 0) &
   done
   wait
)

set -m(et fonction shell POSIX facultative, fonctionnalité shell Unix requise) exécute les travaux dans leur propre groupe de processus. Dans bash, yash, zsh, mksh, c'est des emplois du sous - shell où set -mest activée si l'extérieur (...)et tous les processus créés dans ce seront placés dans le même groupe de processus.

Pour dashet d'autres ashshells basés, cela ne fonctionne qu'au niveau du processus de shell de niveau supérieur. Donc, ce code fonctionnera à moins qu'il ne soit placé dans un sous-shell.

Cela ne fonctionnera pas du tout dans AT&T kshou l'ancien shell SysV / Bourne.

kill 0 envoie un signal SIGTERM à tous les membres du groupe de processus en cours.

Stéphane Chazelas
la source
En bash. Pourquoi j'ai inclus un shebang - la coquille requise n'est pas claire. Bonne réponse
Jim Mcnamara
@jimmcnamara, qui travaille dans bash, dash, yash, mksh, zsh. Fondamentalement, n'importe quel shell POSIX, mais AT&T ksh. set -mest (sous-) spécifié dans POSIX mais en tant que fonctionnalité facultative.
Stéphane Chazelas
J'utilise Solaris. / bin / sh ne volera pas.
Jim Mcnamara du
@jimmcnamara, no / bin / sh sur Solaris 10 et avant est le shell Bourne (pas un shell POSIX), et sur 11, AT&T ksh. Comme je l'ai dit, cela fonctionne en bash, dash, yash, mksh, zsh.
Stéphane Chazelas
1
@mikeserv, cela ramènerait le processus à 1, mais ne le retirerait pas du groupe de processus. kill 0tue tous les membres du groupe de processus quel que soit leur parent. Voir ps -jpour voir les identifiants des groupes de processus.
Stéphane Chazelas
3

Ceci est un exemple. JOUER avec ce premier pour obtenir exactement ce dont vous avez besoin. Il ne peut pas beaucoup se casser tel quel.

#!/bin/bash
# Example of killing off all children

> killfile
> outfile.err
kill_em()
{
   echo 'killing all children ' > 2
   while read pid
   do
      kill -0 $pid && kill -9 $pid  # if still running kill it
   done < killfile
   exit 1
}

export grandparentpid=$$
trap 'kill_em' 6
for i in 2 2 3 4 5 6 7 8 9 10
do
        ( sleep $i && ls oinkle  >> outfile 2>> outfile.err &
          pid=$!
          echo $pid >> killfile
          wait $!
          [ $? -ne 0 ] && kill -6 $grandparentpid
        ) &
done
wait

Ceci est configuré pour échouer délibérément car ls oinkleéchouera (sur ma machine).

Lorsque vous obtenez ce dont vous avez besoin après avoir bricolé avec le script de démarrage --- Changement:

for i in 2 2 3 4 5 6 7 8 9 10

à:

for part in input_* 

changement:

sleep $i && ls oinkle 

à:

python3 $part 

Les redirections sont là pour enregistrer les journaux. Vous n'en voudrez peut-être pas.

Jim Mcnamara
la source
C'est un peu racé. Si l'un des travaux échoue avant que tous les autres aient démarré, il se killfilepeut que votre ne contienne pas tous les pids des travaux démarrés.
Stéphane Chazelas
Quelques mauvaises pratiques comme: les variables non cotées, l'utilisation des numéros de signaux au lieu des noms, utiliser le signal 6 (ABRT sur Linux amd64 par exemple) au lieu de USR1 / USR2 comme signal utilisateur, [ $? -ne 0 ]...
Stéphane Chazelas