Comment obtenir la liste de tous les processus enfants générés par un script

9

Le contexte:

Les utilisateurs me fournissent leurs scripts personnalisés à exécuter. Ces scripts peuvent être de toute sorte comme des scripts pour démarrer plusieurs programmes GUI, des services backend. Je n'ai aucun contrôle sur la façon dont les scripts sont écrits. Ces scripts peuvent être de type bloquant, c'est-à-dire que l'exécution attend que tous les processus enfants (programmes exécutés de manière séquentielle) se terminent

#exaple of blocking script
echo "START"
first_program 
second_program 
echo "DONE"

ou de type non bloquant, c'est-à-dire ceux qui bifurquent le processus enfant en arrière-plan et sortent quelque chose comme

#example of non-blocking script
echo "START"
first_program &
second_program &
echo "DONE"

Qu'est-ce que j'essaie de réaliser?

Les scripts fournis par l'utilisateur peuvent être de l'un des deux types ci-dessus ou mélanger les deux. Mon travail consiste à exécuter le script et à attendre que tous les processus démarrés par celui-ci se terminent puis à arrêter le nœud. S'il s'agit d'un type de blocage, la casse est simple, c'est-à-dire obtenir le PID du processus d'exécution de script et attendre que ps -ef | grep -ef PID n'ait plus d'entrées. Les scripts non bloquants sont ceux qui me posent problème

Existe-t-il un moyen d'obtenir la liste des PID de tous les processus enfants générés par l'exécution d'un script? Tous les pointeurs ou indices seront très appréciés

irraju
la source
Je ne pense pas que ce soit possible après la fin du script parent, sauf si vous pouvez capturer le PID du parent. Si vous lancez les scripts, vous pouvez les envelopper dans quelque chose comme pid$(foo.sh; echo $!)qui vous donnera le PID de foo.shsorte que vous puissiez ensuite utiliser ps --ppid. Ça marchera?
terdon
2
Les scripts doivent-ils s'exécuter sous l'UID de l'utilisateur auteur? Sinon, pouvez-vous créer un utilisateur fictif juste à cette fin? Vous n'en auriez même pas besoin grep, juste ps –udummy_user. Regardez également les groupes de processus.
Scott
Il s'agit plus d'un type de solution de contournement que d'une solution à votre question initiale: ouvrez une nouvelle session bash. Vous pouvez répertorier tous les processus générés à partir de ce shell en utilisant pssans aucun argument (devrait être uniquement bashet psau début). Commencez votre script là-bas. Une fois l'opération terminée, attendez que ps | wc -lla valeur attendue soit atteinte.
Tim

Réponses:

8

Pour répondre directement à votre question, la commande

jobs -p

vous donne la liste de tous les processus enfants.

Alternative n ° 1

Mais dans votre cas, il pourrait être plus facile d'utiliser simplement la commande waitsans aucun paramètre:

first_program &
second_program &
wait

Cela attendra la fin de TOUS les processus enfants.

Alternative n ° 2

Une autre alternative consiste à utiliser $!pour obtenir le PID du dernier programme et peut-être accumuler dans une variable, comme ceci:

pids=""
first_program &
pids="$pids $!"
second_program &
pids="$pids $!"

puis utilisez wait avec cela (c'est au cas où vous ne voulez attendre qu'un sous-ensemble de vos processus enfants):

wait $pids

Alternative n ° 3

OU, si vous ne voulez attendre que TOUT processus soit terminé, vous pouvez utiliser

wait -n $pids

Infos bonus

Si vous souhaitez qu'un sigterm de votre script bash ferme également vos processus enfants, vous devrez propager le signal avec quelque chose comme ceci (mettez-le quelque part en haut, avant de démarrer un processus):

trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
Vlad A Ionescu
la source
1

Merci les gars pour vos réponses .. J'ai obtenu la solution sur stackoverflow

Vous pouvez utiliser attendre pour attendre la fin de tous les processus d'arrière-plan démarrés par le script utilisateur. Étant donné que l'attente ne fonctionne que sur les enfants du shell actuel, vous devrez source leur script au lieu de l'exécuter en tant que processus distinct.

(script utilisateur source; attendez)

Le sourcing du script dans un sous-shell explicite devrait simuler le démarrage d'un nouveau processus assez étroitement. Sinon, vous pouvez également mettre en arrière-plan le sous-shell, ce qui force le démarrage d'un nouveau processus, puis attendre qu'il se termine.

(script utilisateur source; attendez) & attendez

Voici le lien pour la réponse originale de @chepner: /programming/18663196/how-to-get-list-of-all-child-process-spawned-by-a-script/18663969?noredirect = 1 # 18663969

irraju
la source
3
J'y ai également pensé, mais cette solution échoue lorsque le script utilisateur démarre un indice en arrière-plan qui démarre ensuite un autre script en arrière-plan. Votre waitcommande attendra alors les enfants du script utilisateur mais pas ses petits-enfants.
Tim
@Tim vient de réaliser que :(
irraju