web-dev-qa-db-fra.com

Quel est le meilleur moyen d'envoyer un signal à tous les membres d'un groupe de processus?

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.

398
Adam Peck

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.

279
Norman Ramsey

É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.

Explication

  • 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
    Mais:
    • 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).

Autres lignes de commande

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)

Limitation

  • Comme remarqué par davide et Hubert Kario , lorsque 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.
  • Par conséquent, veillez à exécuter la commande en utilisant un processus ayant un Process-Group-ID différent.

Longue histoire

> 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

Conclusion

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).

181
olibre
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.

162
Onlyjob

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 $@
99
zhigang

La commande rkill de pslist envoie le signal indiqué (ou SIGTERM par défaut) au processus spécifié et à tous ses descendants:

rkill [-SIG] pid/name...
15
Onlyjob

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
11
Kim Stebel

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
9
x-yuri

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.

9
Tomasz Werszko

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
9
brad.lane

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. 

8
hajamie

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 ' ' )
8
tanager

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 --

7
Steven Penny

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
}
5

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()
5
genericdave

Si vous voulez tuer un processus par son nom:

killall -9 -g someprocessname

ou

pgrep someprocessname | xargs pkill -9 -g
4
Mika Vatanen

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
}
4
davide

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.

3
solidsnack

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//,/ }
3
marekdef

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.

1
Whinger

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. 

1
Peter Steier

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
1
Peter Steier

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
1
Orsiris de Jong

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
0
jens

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 )))
0
Morgan

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é.

0
Donald Duck

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)'
0
lyman

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

0
Nullpointer

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. 

Explication:

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. 

0
CoderBlue
ps -o pid= --ppid $PPID | xargs kill -9 
0
Harpreet

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,

Tuer un processus enfant dans un script Shell

0
Hemant Thorat