Je veux tuer tout un arbre de processus. Quelle est la meilleure façon de faire cela en utilisant un langage de script courant? Je cherche une solution simple.
Vous ne dites pas si l’arbre que vous voulez tuer est un groupe de processus unique. (Cela est souvent le cas si l'arborescence est le résultat d'une conversion à partir d'un serveur ou d'une ligne de commande Shell.) Vous pouvez découvrir des groupes de processus à l'aide de GNU ps comme suit:
ps x -o "%p %r %y %x %c "
Si vous souhaitez supprimer un groupe de processus, utilisez simplement la commande kill(1)
, mais au lieu de lui attribuer un numéro de processus, attribuez-lui le négation du numéro de groupe. Par exemple, pour tuer tous les processus du groupe 5112, utilisez kill -TERM -- -5112
.
Éliminez tous les processus appartenant au même arborescence de processus à l'aide de ID du groupe de processus (PGID
).
kill -- -$PGID
Utiliser le signal par défaut (TERM
= 15)kill -9 -$PGID
Utilisez le signal KILL
(9)Vous pouvez récupérer la PGID
à partir de n'importe quel ID de processus (PID
) du même arbre de processus _
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signal TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signal KILL
)Un merci spécial à tanager et Speakus pour leurs contributions sur $PID
espaces restants et la compatibilité OSX.
kill -9 -"$PGID"
=> Envoyez le signal 9 (KILL
) à tous les enfants et petits-enfants ...PGID=$(ps opgid= "$PID")
=> Récupérez le Process-Group-ID de n'importe quel Process-ID de l'arborescence, pas seulement le Process-Parent-ID. Une variante de ps opgid= $PID
est ps -o pgid --no-headers $PID
, où pgid
peut être remplacé par pgrp
. ps
insère des espaces de début lorsque PID
est inférieur à cinq chiffres et aligné à droite comme indiqué par tanager . Vous pouvez utiliser:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
à partir d’OSX affiche toujours l’en-tête, donc Speakus propose:PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]*
imprime uniquement les chiffres successifs (n'imprime ni espaces ni en-têtes alphabétiques).PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID" # kill -15
kill -INT -"$PGID" # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID" # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID" # restart a stopped process (above signals do not kill it)
sleep 2 # wait terminate process (more time if required)
kill -KILL -"$PGID" # kill -9 if it does not intercept signals (or buggy)
kill
est invoqué par un processus appartenant au même arbre, kill
risque de se tuer avant de mettre fin à l'abattage complet de l'arbre.> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"
> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"
Exécutez l’arbre de processus en arrière-plan en utilisant '&'
> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)
> PID=$! # get the Parent Process ID
> PGID=$(ps opgid= "$PID") # get the Process Group ID
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28969 Ss 33021 0:00 -bash
28349 28957 28957 28349 pts/3 28969 S 33021 0:00 \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28969 S 33021 0:00 | | | \_ sleep 9999
28958 28963 28957 28349 pts/3 28969 S 33021 0:00 | | \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28957 28959 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28969 S 33021 0:00 | | \_ sleep 9999
28959 28962 28957 28349 pts/3 28969 S 33021 0:00 | \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28969 S 33021 0:00 | \_ sleep 9999
28349 28969 28969 28349 pts/3 28969 R+ 33021 0:00 \_ ps fj
La commande pkill -P $PID
ne tue pas le petit-enfant:
> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+ Done ./run-many-processes.sh
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 28987 Ss 33021 0:00 -bash
28349 28987 28987 28349 pts/3 28987 R+ 33021 0:00 \_ ps fj
1 28963 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28962 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28961 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
1 28960 28957 28349 pts/3 28987 S 33021 0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3 28987 S 33021 0:00 \_ sleep 9999
La commande kill -- -$PGID
supprime tous les processus, y compris le petit-enfant.
> kill -- -"$PGID" # default signal is TERM (kill -15)
> kill -CONT -"$PGID" # awake stopped processes
> kill -KILL -"$PGID" # kill -9 to be sure
> ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
28348 28349 28349 28349 pts/3 29039 Ss 33021 0:00 -bash
28349 29039 29039 28349 pts/3 29039 R+ 33021 0:00 \_ ps fj
Je remarque dans cet exemple que PID
et PGID
sont égaux (28957
).
C’est pourquoi j’ai pensé que kill -- -$PID
était suffisant. Mais dans le cas où le processus est créé dans une variable Makefile
, le ID de processus est différent du ID de groupe.
Je pense que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
est la meilleure astuce simple pour supprimer tout un arbre de processus lorsqu'il est appelé depuis un autre ID groupe (un autre arbre de processus).
pkill -TERM -P 27888
Cela supprimera tous les processus portant l'ID de processus parent 27888.
Ou plus robuste:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
ce calendrier tue 33 secondes plus tard et demande poliment aux processus de se terminer.
Voir cette réponse pour mettre fin à tous les descendants.
Pour tuer une arborescence de processus de manière récursive, utilisez killtree ():
#!/bin/bash
killtree() {
local _pid=$1
local _sig=${2:--TERM}
kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
killtree ${_child} ${_sig}
done
kill -${_sig} ${_pid}
}
if [ $# -eq 0 -o $# -gt 2 ]; then
echo "Usage: $(basename $0) <pid> [signal]"
exit 1
fi
killtree $@
la réponse de Brad est ce que je recommanderais aussi, sauf que vous pouvez supprimer awk
si vous utilisez l'option --ppid
avec ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
Version modifiée de la réponse de zhigang:
#!/usr/bin/env bash
set -eu
killtree() {
local pid
for pid; do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid); do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done
}
cpids() {
local pid=$1 options=${2:-} space=${3:-}
local cpid
for cpid in $(pgrep -P $pid); do
echo "$space$cpid"
if [[ "${options/a/}" != "$options" ]]; then
cpids $cpid "$options" "$space "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a
J'utilise une version légèrement modifiée d'une méthode décrite ici: https://stackoverflow.com/a/5311362/563175
Alors ça ressemble à ça:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
où 24901 est le PID du parent.
Il a l'air assez moche mais fait son travail parfaitement.
si vous savez transmettre le pid du processus parent, voici un script Shell qui devrait fonctionner:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done
Pour ajouter à la réponse de Norman Ramsey, il peut être intéressant de regarder setsid si vous souhaitez créer un groupe de processus.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
La fonction setsid () doit créer un nouvelle session, si le processus appelant est pas un chef de groupe de processus. Sur renvoyer le processus d'appel doit être le chef de session de ce nouveau session, constitue le groupe de processus responsable d'un nouveau groupe de processus, et ne doit pas avoir de terminal de contrôle . L'ID de groupe de processus de l'appelant processus doit être égal à ID de processus du processus appelant. Le Le processus d’appel sera le seul processus dans le nouveau groupe de processus et le seul processus dans la nouvelle session.
Cela signifie que vous pouvez créer un groupe à partir du processus de démarrage. J'ai utilisé cela en php afin de pouvoir tuer tout un arbre de processus après l'avoir lancé.
Cela peut être une mauvaise idée. Je serais intéressé par les commentaires.
Je ne peux pas commenter (pas assez de réputation), alors je suis obligé d'ajouter une nouvelle réponse , même si ce n'est pas vraiment une réponse.
Il y a un petit problème avec la réponse par ailleurs très gentille et complète donnée par @olibre le 28 février. La sortie de ps opgid= $PID
contiendra les espaces de début pour un PID de moins de cinq chiffres car ps
justifie la colonne (aligne les chiffres). Cela se traduit par un signe négatif dans la ligne de commande entière, suivi d'un espace, suivi du PID du groupe. La solution simple est d’aligner ps
à tr
pour supprimer des espaces:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Inspiré par commentaire de ysth
kill -- -PGID
au lieu de lui attribuer un numéro de processus, donnez-lui la négation du groupe nombre. Comme d'habitude avec presque toutes les commandes, si vous voulez un argument normal que commence par un
-
pour ne pas être interprété comme un commutateur, précédez-le par--
La fonction Shell suivante est similaire à beaucoup d’autres réponses, mais elle fonctionne à la fois sous Linux et BSD (OS X, etc.) sans dépendances externes telles que pgrep
:
killtree() {
local parent=$1 child
for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
killtree $child
done
kill $parent
}
C'est très facile de faire cela avec python en utilisant psutil . Installez simplement psutil avec pip, puis vous disposez d'une suite complète d'outils de manipulation de processus:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Si vous voulez tuer un processus par son nom:
killall -9 -g someprocessname
ou
pgrep someprocessname | xargs pkill -9 -g
Basé sur la réponse de zhigang, cela évite l'auto-tuerie:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
Voici une variante de la réponse de @ zhigang qui se passe de AWK, en s'appuyant uniquement sur les possibilités d'analyse natives de Bash:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
Cela semble fonctionner correctement sur les Mac et Linux. Dans les situations où vous ne pouvez pas compter sur la capacité de gérer des groupes de processus, comme lors de l'écriture de scripts pour tester un logiciel devant être construit dans plusieurs environnements, cette technique de parcours en arbre est certainement utile.
Voici ma version de la suppression de tous les processus enfants à l'aide du script bash . Elle n'utilise pas la récursivité et dépend de la commande pgrep.
Utilisation
killtree.sh PID SIGNAL
Contenu de killtrees.sh
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
Vieille question, je sais, mais toutes les réponses semblent continuer à appeler ps, ce que je n’aimais pas.
Cette solution basée sur awk ne nécessite pas de récursivité et appelle ps une seule fois.
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
Ou sur une seule ligne:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
En gros, l’idée est de construire un tableau (a) d’entrées parent: enfant, puis de parcourir le tableau pour trouver les enfants des parents correspondants et les ajouter à notre liste de parents (p) au fur et à mesure.
Si vous ne voulez pas tuer le processus de niveau supérieur, faites alors
sub(/[0-9]*/, "", p)
juste avant que la ligne system () le supprime de l'ensemble de mise à mort.
Gardez à l'esprit qu'il existe une situation de concurrence critique, mais c'est vrai (autant que je sache) de toutes les solutions. Il fait ce dont j'avais besoin car le script pour lequel j'en avais besoin ne crée pas beaucoup d'enfants de courte durée.
Un exercice pour le lecteur consisterait à en faire une boucle en deux passes: après la première passe, envoyez SIGSTOP à tous les processus de la liste p, puis en boucle pour exécuter ps à nouveau et après la seconde passe, envoyez SIGTERM, puis SIGCONT. Si vous ne vous souciez pas des fins de Nice, alors la deuxième passe pourrait bien être SIGKILL, je suppose.
Je développe la solution de zhigang, xyuri et de solidsneck plus loin:
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
exit 1 ;
fi ;
_pid=$1
_sig=${2:-TERM}
# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;
function _killtree () {
local _children
local _child
local _success
if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _child in ${_children}; do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?)) ;
done ;
if test $_success -eq 0 ; then
kill -$3 $2
fi ;
# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0 ;
return $?
}
_killtree $$ $_pid $_sig
Cette version évitera de tuer ses ancêtres - ce qui provoque une avalanche de processus enfants dans les solutions précédentes.
Les processus sont correctement arrêtés avant la détermination de la liste d'enfants, de sorte qu'aucun nouvel enfant ne soit créé ou ne disparaisse.
Après avoir été tués, les travaux arrêtés doivent continuer à disparaître du système.
Il vaut probablement mieux tuer le parent avant les enfants; sinon, le parent pourrait probablement engendrer de nouveaux enfants avant qu'il ne soit tué. Ceux-ci survivront au massacre.
Ma version de ps est différente de celle ci-dessus; peut-être trop vieux, donc l'étrange grepping ...
Utiliser un script shell à la place d'une fonction shell présente de nombreux avantages ...
Cependant, il s’agit essentiellement d’une idée de zhigang
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done
Ce qui suit a été testé sur FreeBSD, Linux et MacOS X et ne dépend que de pgrep et de kill (les versions ps -o ne fonctionnent pas sous BSD). Le premier argument est le pid du parent pour lequel les enfants doivent être terminés. Le second argument est un booléen pour déterminer si le pid parent doit également être terminé.
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
Cela enverra SIGTERM à n’importe quel processus enfant/petit-enfant dans un script Shell et si SIGTERM n’aboutit pas, il attendra 10 secondes avant d’envoyer kill.
Réponse précédente:
Ce qui suit fonctionne aussi mais va tuer le shell sur BSD.
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
Dans sh, la commande jobs listera les processus en arrière-plan. Dans certains cas, il peut être préférable de supprimer d’abord le processus le plus récent, par exemple. le plus ancien a créé un socket partagé. Dans ces cas, triez les PID dans l'ordre inverse. Parfois, vous voulez attendre que les tâches écrivent quelque chose sur le disque ou des choses de ce genre avant de les arrêter.
Et ne tue pas si tu n'es pas obligé!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Si vous connaissez le pid de la chose que vous voulez tuer, vous pouvez généralement utiliser l'identifiant de session et tout ce qui se trouve dans la même session. Je revérifierais, mais je l'ai utilisé pour les scripts commençant par rsyncs dans des boucles que je veux mourir, et non pas pour en démarrer un autre (à cause de la boucle) comme si je tuerais simplement rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Si vous ne connaissez pas le pid, vous pouvez toujours nicher davantage
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
Merci pour votre sagesse, les gars. Mon script laissait certains processus enfants à la sortie et le negation tip facilitait les choses. J'ai écrit cette fonction pour l'utiliser dans d'autres scripts si nécessaire:
# kill my group's subprocesses: killGroup
# kill also myself: killGroup -x
# kill another group's subprocesses: killGroup N
# kill that group all: killGroup -x N
# N: PID of the main process (= process group ID).
function killGroup () {
local prid mainpid
case $1 in
-x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
"") mainpid=$$ ;;
*) mainpid=$1 ;;
esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return
}
À votre santé.
Je sais que c'est vieux, mais c'est la meilleure solution que j'ai trouvée:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}
si vous avez pstree et Perl sur votre système, vous pouvez essayer ceci:
Perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'
Ce script fonctionne aussi:
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
Tuer le groupe en utilisant uniquement le nom du processus appartenant au groupe:
kill -- -$(ps -ae o pid,pgrp,cmd | grep "[e]xample.py" | awk '{print $2}' | tail -1)
Ceci est une modification de la réponse d'Olibre mais vous n'avez pas besoin de connaître le PID, mais simplement le nom d'un membre du groupe.
Pour obtenir l'identifiant du groupe, vous devez exécuter la commande ps en utilisant les arguments ci-dessous. Grep le pour votre commande, mais formatez example.py avec des guillemets et utilisez le crochet pour la première lettre (cela filtre la commande grep elle-même), puis filtrez-le. awk pour obtenir le deuxième champ qui est l'identifiant du groupe. La queue -1 supprime les identifiants de groupe en double. Vous mettez tout cela dans une variable utilisant la syntaxe $ () et le tour est joué - vous obtenez l'identifiant du groupe. Donc, vous substituez ce $ (désordre) à cet -groupid ci-dessus.
ps -o pid= --ppid $PPID | xargs kill -9
Tuer le processus enfant dans le script Shell:
Nous avons souvent besoin de tuer des processus enfants qui sont suspendus ou bloqués pour une raison quelconque. par exemple. Problème de connexion FTP.
Il y a deux approches,
1) Créer un nouveau parent distinct pour chaque enfant, qui surveillera et éliminera le processus enfant une fois le délai d’expiration écoulé.
Créez test.sh comme suit,
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
et observez les processus dont le nom est 'test' dans un autre terminal en utilisant la commande suivante.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Le script ci-dessus créera 4 nouveaux processus enfants et leurs parents. Chaque processus enfant s'exécutera pendant 10 secondes. Mais une fois le délai de 5 secondes atteint, leurs processus parents respectifs tueront ces enfants . Ainsi, les enfants ne pourront pas exécuter leur exécution (10 secondes). Jouez autour de ces horaires (commutateurs 10 et 5) pour voir un autre comportement. Dans ce cas, l'enfant finira l'exécution en 5 secondes avant qu'il ne dépasse 10 secondes.
2) Laissez le moniteur parent actuel et tuer le processus enfant une fois le délai d’expiration écoulé. Cela ne créera pas de parent séparé pour surveiller chaque enfant. Vous pouvez également gérer correctement tous les processus enfants au sein du même parent.
Créez test.sh comme suit,
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
CMD_TIME=15;
for CMD in ${CMDs[*]}; do
(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
CPIDs[$!]="$RN";
sleep 1;
done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
declare -A TMP_CPIDs;
for PID in "${!CPIDs[@]}"; do
echo "Checking "${CPIDs[$PID]}"=>"$PID;
if ps -p $PID > /dev/null ; then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};
else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$PID]=${TMP_CPIDs[$PID]};
done
unset TMP_CPIDs;
if [ $CNT -gt $CNT_TIME_OUT ]; then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill -- -$GPID;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
et observez les processus dont le nom est 'test' dans un autre terminal en utilisant la commande suivante.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Le script ci-dessus créera 4 nouveaux processus enfants. Nous stockons les pids de tous les processus enfants et les bouclons pour vérifier s'ils ont terminé leur exécution ou s'ils sont toujours en cours d'exécution. Le processus enfant sera exécuté jusqu'au moment CMD_TIME. Mais si le délai d'expiration de CNT_TIME_OUT est dépassé, tous les enfants seront tués par le processus parent . Vous pouvez changer le minutage et jouer avec le script pour voir le comportement . Un inconvénient de cette approche est qu'elle utilise l'identifiant de groupe pour tuer tous les enfants. arbre. Mais le processus parent lui-même appartient au même groupe, il sera donc également tué.
Vous devrez peut-être affecter un autre identifiant de groupe au processus parent si vous ne voulez pas que le parent soit tué.
Plus de détails ici,