Je veux tuer tout un arbre de processus. Quelle est la meilleure façon de procéder en utilisant des langages de script courants? Je cherche une solution simple.
chronos
ou herodes
.
Je veux tuer tout un arbre de processus. Quelle est la meilleure façon de procéder en utilisant des langages de script courants? Je cherche une solution simple.
chronos
ou herodes
.
Réponses:
Vous ne dites pas si l'arbre que vous voulez tuer est un seul groupe de processus. (C'est souvent le cas si l'arborescence est le résultat d'un fork d'un démarrage de serveur ou d'une ligne de commande shell.) Vous pouvez découvrir des groupes de processus en utilisant GNU ps comme suit:
ps x -o "%p %r %y %x %c "
Si c'est un groupe de processus que vous voulez tuer, utilisez simplement la kill(1)
commande mais au lieu de lui donner un numéro de processus, donnez-lui la négation du numéro de groupe. Par exemple, pour tuer tous les processus du groupe 5112, utilisez kill -TERM -- -5112
.
pgrep
peut offrir un moyen plus simple de trouver l'ID de groupe de processus. Par exemple, pour tuer le groupe de processus de my-script.sh, exécutez kill -TERM -$(pgrep -o my-script.sh)
.
ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
Tuez tous les processus appartenant à la même arborescence de processus à l'aide de l' ID de 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 le à PGID
partir de n'importe quel ID de processus ( PID
) de la même arborescence 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
les espaces restants et la compatibilité OSX.
kill -9 -"$PGID"
=> Envoyer le signal 9 ( KILL
) à tous les enfants et petits-enfants ...PGID=$(ps opgid= "$PID")
=> Récupérer l' ID de groupe de processus à partir de n'importe quel ID de processus de l'arborescence, pas seulement l' ID de parent de processus . Une variation de ps opgid= $PID
est ps -o pgid --no-headers $PID
où pgid
peut être remplacée par pgrp
. grep -o [0-9]*
imprime uniquement les chiffres successifs (n'imprime pas les espaces ni les 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, il kill
risque de se tuer avant de mettre fin à la destruction de l'arbre entier.> 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'arborescence des processus en arrière-plan à l'aide de '&'
> ./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
tue 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 PID
et suis PGID
égal ( 28957
).
C'est pourquoi je pensais à l'origine que kill -- -$PID
c'était suffisant. Mais dans le cas où le processus est fraient dans un Makefile
l' ID de processus est différent de l' ID de groupe .
Je pense que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
c'est la meilleure astuce simple pour tuer un arbre de processus entier lorsqu'il est appelé à partir d'un ID de groupe différent (un autre arbre de processus).
kill
devrait toujours envoyer le signal à l'arbre entier avant de recevoir son propre signal. Mais dans certaines circonstances / implémentations spécifiques, il kill
peut s’envoyer le signal, être interrompu, puis recevoir son propre signal. Cependant, le risque doit être suffisamment minime et peut être ignoré dans la plupart des cas, car d'autres bogues doivent se produire avant celui-ci. Ce risque peut-il être ignoré dans votre cas? De plus, d'autres réponses ont ce bogue commun (une kill
partie de l'arbre de processus est en cours de suppression). J'espère que cette aide .. Cheers;)
man
ça. D'un autre côté, si vous voulez tuer le processus gandchild du processus enfant, kill -- -$pid
cela ne fonctionnera pas. Ce n'est donc pas une solution générique.
child.sh
.
pkill -TERM -P 27888
Cela tuera tous les processus qui ont l'ID de processus parent 27888.
Ou plus robuste:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
qui doit tuer 33 secondes plus tard et demander poliment aux processus de se terminer.
Voir cette réponse pour mettre fin à tous les descendants.
pkill -P
envoie le signal à l'enfant uniquement => le petit-enfant ne reçoit pas le signal => J'ai donc écrit une autre réponse pour l'expliquer.
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Pour tuer une arborescence de processus récursivement, 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 $@
--
arguments pour ps
ne pas fonctionner sous OS X. Pour le faire fonctionner, remplacez la ps
commande par: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
où _regex
est défini avant la for
boucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
fonctionne de manière fiable?
ps
ne prend pas en charge --ppid
, on peut utiliser à la pgrep -P ${_pid}
place
La réponse de Brad est ce que je recommanderais aussi, sauf que vous pouvez supprimer awk
complètement si vous utilisez l' --ppid
option pour ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
si vous savez passer 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
J'utilise une version légèrement modifiée d'une méthode décrite ici: https://stackoverflow.com/a/5311362/563175
Donc ç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.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
à pstree
, donc les longues lignes ne sont pas tronquées; il peut être rendu plus simple à lire également avec kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(pas besoin de convertir \n
en espace car cela fonctionnera bien), merci!
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
wait $pid
que sur les processus que vous avez démarrés, pas sur tous les processus, donc ce n'est pas une solution générique
wait
supprimera le Terminated
message s'il s'agit d'un enfant.
Je ne peux pas commenter (pas assez de réputation), donc je suis obligé d'ajouter une nouvelle réponse , même si ce n'est pas vraiment une réponse.
Il y a un léger problème avec la réponse par ailleurs très agréable et approfondie donnée par @olibre le 28 février. La sortie de ps opgid= $PID
contiendra des espaces de tête pour un PID inférieur à cinq chiffres car ps
justifie la colonne (alignez les chiffres). Dans toute la ligne de commande, cela se traduit par un signe négatif, suivi par des espaces, suivi du groupe PID. La solution simple consiste à diriger ps
vers tr
pour supprimer les espaces:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Pour compléter la réponse de Norman Ramsey, il peut être utile 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 une nouvelle session, si le processus appelant n'est pas un leader de groupe de processus. A son retour, le processus appelant sera le chef de session de cette nouvelle session, sera le chef de groupe de processus d'un nouveau groupe de processus et n'aura aucun terminal de contrôle. L'ID de groupe de processus du processus appelant doit être égal à l'ID de processus du processus appelant. Le processus appelant doit être le seul processus du nouveau groupe de processus et le seul processus de la nouvelle session.
Ce que je suppose que vous pouvez créer un groupe à partir du processus de démarrage. Je l'ai utilisé en php afin de pouvoir tuer tout un arbre de processus après l'avoir démarré.
Cela peut être une mauvaise idée. Je serais intéressé par des commentaires.
Inspiré par le commentaire d' Ysth
kill -- -PGID
au lieu de lui donner un numéro de processus, donnez-lui la négation du numéro de groupe. Comme d'habitude avec presque toutes les commandes, si vous voulez qu'un argument normal commençant par un
-
ne soit pas interprété comme un commutateur, faites-le précéder de--
PGID
partir PID
. Qu'est-ce que tu penses? Vive
La fonction shell suivante est similaire à la plupart des autres réponses, mais elle fonctionne à la fois sur Linux et BSD (OS X, etc.) sans dépendances externes comme 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
}
$child
cette fonction afin de ne pas perturber les autres variables (non locales) du même nom et de garantir que la valeur de la variable locale est nettoyée après la fin de la fonction.
C'est super facile de le faire avec python en utilisant psutil . Installez simplement psutil avec pip et 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
Ceci est ma version de tuer tous les processus enfants en utilisant le script bash. Il n'utilise pas de 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//,/ }
Voici une variante de la réponse de @ zhigang qui se passe d'AWK, en se basant uniquement sur les possibilités d'analyse native 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"
}
Il semble bien fonctionner à la fois sur 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 qui doit être construit dans plusieurs environnements - cette technique d'arborescence est certainement utile.
Merci pour votre sagesse, les amis. Mon script laissait certains processus enfants à la sortie et la pointe de négation a rendu les choses plus faciles. J'ai écrit cette fonction pour être utilisée 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é.
Il vaut probablement mieux tuer le parent avant les enfants; sinon, le parent peut probablement engendrer de nouveaux enfants avant qu'il ne soit tué lui-même. Ceux-ci survivront au massacre.
Ma version de ps est différente de celle ci-dessus; peut-être trop vieux, donc l'étrange avidité ...
Utiliser un script shell au lieu d'une fonction shell présente de nombreux avantages ...
Cependant, c'est essentiellement l'idée de zhigangs
#!/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 kill (les versions ps -o ne fonctionnent pas sous BSD). Le premier argument est le parent pid dont les enfants doivent être supprimés. le deuxième 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 à tout processus enfant / petit-enfant dans un script shell et si SIGTERM ne réussit pas, il attendra 10 secondes, puis enverra kill.
Réponse antérieure:
Ce qui suit fonctionne également mais tuera le shell lui-même 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
Je développe encore la solution de zhigang, xyuri et solidsneck:
#!/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 son ascendance - ce qui provoque un déluge de processus enfants dans les solutions précédentes.
Les processus sont correctement arrêtés avant que la liste d'enfants ne soit déterminée, afin qu'aucun nouvel enfant ne soit créé ou disparaisse.
Après avoir été tués, les emplois suspendus doivent continuer à disparaître du système.
Vieille question, je sais, mais toutes les réponses semblent continuer d'appeler ps, ce que je n'ai pas aimé.
Cette solution basée sur awk ne nécessite pas de récursivité et n'appelle ps qu'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)}'
Fondamentalement, l'idée est de créer un tableau (a) d'entrées parent: enfant, puis de parcourir le tableau pour trouver des enfants pour nos parents correspondants, en les ajoutant à notre liste de parents (p) au fur et à mesure.
Si vous ne voulez pas tuer le processus de niveau supérieur, alors faites
sub(/[0-9]*/, "", p)
juste avant que la ligne system () ne la supprime du kill set.
Gardez à l'esprit qu'il y a une condition de concurrence ici, mais c'est vrai (pour autant que je puisse voir) de toutes les solutions. Il fait ce dont j'avais besoin parce que le script dont j'avais besoin ne crée pas beaucoup d'enfants de courte durée.
Un exercice pour le lecteur serait d'en faire une boucle en 2 passes: après la première passe, envoyez SIGSTOP à tous les processus de la liste p, puis bouclez pour relancer ps et après la deuxième passe envoyez SIGTERM, puis SIGCONT. Si vous ne vous souciez pas des fins agréables, le second passage pourrait simplement être SIGKILL, je suppose.
Si vous connaissez le pid de la chose que vous voulez tuer, vous pouvez généralement aller à partir de l'ID de session et de tout dans la même session. Je revérifierais, mais je l'ai utilisé pour les scripts démarrant rsyncs dans des boucles que je veux mourir, et pas pour en démarrer une autre (à cause de la boucle) comme si je venais de tuer kills rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Si vous ne connaissez pas le pid, vous pouvez toujours imbriquer plus
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
vraiment.
kill -15
, ça n'aide pas.
Dans sh, la commande jobs listera les processus d'arrière-plan. Dans certains cas, il peut être préférable de tuer le processus le plus récent en premier, par exemple le plus ancien a créé une socket partagée. Dans ces cas, triez les PID dans l'ordre inverse. Parfois, vous voulez attendre un moment pour que les travaux écrivent quelque chose sur le disque ou des trucs comme ça avant de s'arrêter.
Et ne tuez pas si vous n'êtes pas obligé!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Tuer le processus enfant dans le script shell:
Plusieurs fois, nous devons tuer les processus enfants qui sont suspendus ou bloqués pour une raison quelconque. par exemple. Problème de connexion FTP.
Il existe deux approches,
1) Pour créer un nouveau parent distinct pour chaque enfant qui surveillera et tuera le processus enfant une fois le délai é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 regarder 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 parent respectifs tueront ces enfants. Ainsi, l'enfant ne pourra pas terminer l'exécution (10sec). Jouez autour de ces horaires (commutateurs 10 et 5) pour voir un autre comportement. Dans ce cas, l'enfant terminera l'exécution en 5 secondes avant d'atteindre le délai d'attente de 10 secondes.
2) Laissez le parent actuel surveiller et tuer le processus enfant une fois le délai é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 regarder 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 sont toujours en cours d'exécution. Le processus enfant sera exécuté jusqu'à l'heure CMD_TIME. Mais si le délai d'expiration de CNT_TIME_OUT est atteint, tous les enfants seront tués par le processus parent. Vous pouvez changer de timing et jouer avec le script pour voir le comportement. Un inconvénient de cette approche est qu'elle utilise l'ID de groupe pour tuer tous les arbres enfants. Mais le processus parent lui-même appartient au même groupe, il sera donc également tué.
Vous devrez peut-être attribuer un autre identifiant de groupe au processus parent si vous ne voulez pas que le parent soit tué.
Plus de détails ici,
Ce script fonctionne également:
#/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
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
}