Qual é a melhor maneira de enviar um sinal para todos os membros de um grupo de processos?

Eu quero matar toda uma tree de processos. Qual é a melhor maneira de fazer isso usando qualquer linguagem de script comum? Eu estou procurando uma solução simples.

   

    Você não diz se a tree que você quer matar é um único grupo de processos. (Esse é frequentemente o caso se a tree for o resultado de bifurcação de um início de servidor ou de uma linha de comando de shell.) Você pode descobrir grupos de processos usando o GNU ps da seguinte maneira:

      ps x -o "%p %r %y %x %c " 

    Se for um grupo de processos que você quer matar, apenas use o comando kill(1) mas em vez de dar um número de processo, dê-lhe a negação do número do grupo. Por exemplo, para matar todos os processos no grupo 5112, use kill -TERM -- -5112 .

    Mate todos os processos pertencentes à mesma tree de processos usando o ID do Grupo de Processos ( PGID )

    • kill -- -$PGID Use o sinal padrão ( TERM = 15)
    • kill -9 -$PGID Use o sinal KILL (9)

    Você pode recuperar o PGID de qualquer ID de processo ( PID ) da mesma tree de processo

    • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (sinal TERM )
    • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (sinal KILL )

    Agradecimentos especiais a tanager e Speakus pelas contribuições sobre os espaços remanescentes de $PID e compatibilidade com OSX.

    Explicação

    • kill -9 -"$PGID" => Envie sinal 9 ( KILL ) para todos os filhos e netos …
    • PGID=$(ps opgid= "$PID") => Recupera o ID do grupo de processos de qualquer ID de processo da tree, não apenas a ID pai do processo . Uma variação de ps opgid= $PID é ps -o pgid --no-headers $PID onde pgid pode ser substituído por pgrp .
      Mas:
      • ps insere espaços à esquerda quando o PID tem menos de cinco dígitos e alinhado à direita conforme observado pelo tanager . Você pode usar:
        PGID=$(ps opgid= "$PID" | tr -d ' ')
      • ps do OSX sempre imprime o header, portanto o Speakus propõe:
        PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
    • grep -o [0-9]* imprime somente dígitos sucessivos (não imprime espaços ou headers alfabéticos).

    Outras linhas de comando

     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) 

    Limitação

    • Como notado por davide e Hubert Kario , quando a kill é invocada por um processo pertencente à mesma tree, kill riscos de se matar antes de terminar a matança da tree inteira.
    • Portanto, certifique-se de executar o comando usando um processo com um ID de grupo de processos diferente.

    Longa história

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

    Executar a tree do processo em segundo plano usando ‘&’

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

    O comando pkill -P $PID não mata o neto:

     > 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 

    O comando kill -- -$PGID mata todos os processos, incluindo o neto.

     > 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 

    Conclusão

    Eu observei neste exemplo que o PID e o PGID são iguais ( 28957 ).
    É por isso que pensei originalmente em kill -- -$PID foi o suficiente. Mas no caso em que o processo é gerado dentro de um Makefile o ID do Processo é diferente do ID do Grupo .

    Eu acho que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) é o melhor truque simples para matar toda uma tree de processos quando chamado de um ID de Grupo diferente (outra tree de processos).

     pkill -TERM -P 27888 

    Isso eliminará todos os processos que possuem o ID de processo pai 27888.

    Ou mais robusto:

     CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS 

    Que agendar matança 33 segundo depois e educadamente pedir processos para terminar.

    Veja esta resposta para terminar todos os descendentes.

    Para matar uma tree de processos recursivamente, use 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)  [signal]" exit 1 fi killtree $@ 

    O comando rkill do pacote pslist envia um dado sinal (ou SIGTERM por padrão) para o processo especificado e todos os seus descendentes:

     rkill [-SIG] pid/name... 

    A resposta de Brad é o que eu recomendaria também, exceto que você pode acabar com o awk se você usar a opção --ppid para ps .

     for child in $(ps -o pid -ax --ppid $PPID) do ....... done 

    Se você souber passar o pid do processo pai, aqui está um script de shell que deve funcionar:

     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 

    Eu uso uma versão modificada de um método descrito aqui: https://stackoverflow.com/a/5311362/563175

    Então parece que:

     kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "` 

    onde 24901 é o PID pai.

    Parece muito feio, mas faz o trabalho perfeitamente.

    Versão modificada da resposta 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 

    Para adicionar à resposta de Norman Ramsey, pode valer a pena olhar para o setsid se você quiser criar um grupo de processos.
    http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

    A function setsid () deve criar uma nova session, se o processo de chamada não for um líder do grupo de processos. Após a devolução, o processo de chamada será o líder da session desta nova session, será o líder do grupo de processos de um novo grupo de processos e não terá nenhum terminal de controle. O ID do grupo de processos do processo de chamada deve ser definido como igual ao ID do processo de chamada. O processo de chamada deve ser o único processo no novo grupo de processos e o único processo na nova session.

    O que eu entendo significa que você pode criar um grupo a partir do processo inicial. Eu usei isso no php para poder matar uma tree de processo inteira depois de iniciá-la.

    Isso pode ser uma má ideia. Eu estaria interessado em comentários.

    Eu não posso comentar (não tem reputação suficiente), então eu sou forçado a adicionar uma nova resposta , mesmo que esta não seja realmente uma resposta.

    Há um pequeno problema com a outra resposta muito agradável e completa dada por @olibre em 28 de fevereiro. A saída de ps opgid= $PID conterá espaços ps opgid= $PID para um PID com menos de cinco dígitos porque ps está justificando a coluna (alinhar corretamente o números). Dentro de toda a linha de comando, isso resulta em um sinal negativo, seguido por espaço (s), seguido pelo grupo PID. Solução simples é canalizar ps para tr para remover espaços:

     kill -- -$( ps opgid= $PID | tr -d ' ' ) 

    Inspirado pelo comentário de ysth

     kill -- -PGID 

    em vez de dar um número de processo, dê a negação do número do grupo. Como de costume com quase qualquer comando, se você quiser um argumento normal que comece com um - para não ser interpretado como um switch, preceda-o com --

    A seguinte function de shell é semelhante a muitas outras respostas, mas funciona tanto no Linux quanto no BSD (OS X, etc) sem dependencies externas como o 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 } 

    É super fácil fazer isso com python usando o psutil . Basta instalar o psutil com o pip e, em seguida, você terá um conjunto completo de ferramentas de manipulação de processos:

     def killChildren(pid): parent = psutil.Process(pid) for child in parent.get_children(True): if child.is_running(): child.terminate() 

    Com base na resposta de zhigang, isso evita a auto-morte:

     init_killtree() { local pid=$1 child for child in $(pgrep -P $pid); do init_killtree $child done [ $pid -ne $$ ] && kill -kill $pid } 

    Se você quiser matar um processo pelo nome:

     killall -9 -g someprocessname 

    ou

     pgrep someprocessname | xargs pkill -9 -g 

    Esta é a minha versão de matar todos os processos filhos usando o script bash. Não usa recursion e depende do comando pgrep.

    Usar

     killtree.sh PID SIGNAL 

    Conteúdo 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//,/ } 

    Aqui está uma variação da resposta do @zhigang que faz sem o AWK, contando apenas com as possibilidades de análise nativas do 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" } 

    Parece funcionar bem em Macs e Linux. Em situações em que você não pode confiar em ser capaz de gerenciar grupos de processos – como ao escrever scripts para testar um software que deve ser construído em múltiplos ambientes – esta técnica de caminhada em tree é definitivamente útil.

    É provavelmente melhor matar os pais antes dos filhos; caso contrário, o pai provavelmente gerará novos filhos novamente antes de ser morto. Estes sobreviverão ao assassinato.

    Minha versão do ps é diferente daquela acima; talvez muito velho, portanto o estranho grepping …

    Para usar um script de shell em vez de uma function shell tem muitas vantagens …

    No entanto, é basicamente idéia 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 

    O seguinte foi testado no FreeBSD, Linux e MacOS X e depende apenas do pgrep e kill (as versões ps -o não funcionam no BSD). O primeiro argumento é o pai pid, do qual as crianças precisam ser terminadas. O segundo argumento é um booleano para determinar se o pid pai também deve ser terminado.

     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 

    Isto irá enviar o SIGTERM para qualquer processo filho / neto dentro de um script de shell e se o SIGTERM não for bem sucedido, ele irá esperar 10 segundos e depois enviará kill.


    Resposta anterior:

    O seguinte também funciona, mas irá matar o próprio shell no 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 

    Eu desenvolvo a solução de zhigang, xyuri e solidsneck ainda:

      #!/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 

    Esta versão evitará matar sua ascendência – o que causa uma inundação de processos filhos nas soluções anteriores.

    Os processos são devidamente interrompidos antes que a lista de filhos seja determinada, para que nenhum novo filho seja criado ou desapareça.

    Depois de serem mortos, os trabalhos interrompidos devem continuar a desaparecer do sistema.

    Pergunta antiga, eu sei, mas todas as respostas parecem continuar chamando ps, o que eu não gostei.

    Esta solução baseada no awk não requer recursion e somente chama ps uma vez.

     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 em uma única linha:

     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)}' 

    Basicamente, a idéia é que nós construímos um array (a) de parent: child entries, então fazemos um loop em torno do array, encontrando filhos para nossos pais correspondentes, adicionando-os à nossa lista de pais (p).

    Se você não quer matar o processo de nível superior, então

     sub(/[0-9]*/, "", p) 

    pouco antes de a linha system () removê-lo do kill set.

    Tenha em mente que há uma condição de corrida aqui, mas isso é verdade (até onde eu posso ver) de todas as soluções. Ele faz o que eu precisava, porque o script que eu precisava não cria muitos filhos de curta duração.

    Um exercício para o leitor seria fazer um loop de 2 passos: após o primeiro passo, envie SIGSTOP para todos os processos na lista p, então execute loop para executar ps novamente e depois do segundo passo envie SIGTERM, então SIGCONT. Se você não se importa com finais agradáveis, então o segundo passe pode ser apenas SIGKILL, suponho.

    Eu sei que é antigo, mas essa é a melhor solução que eu encontrei:

     killtree() { for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do echo Terminating: $p kill $p done } 

    Obrigado pela sua sabedoria, pessoal. Meu script estava deixando alguns processos filhos na saída e a dica de negação facilitou as coisas. Eu escrevi essa function para ser usada em outros scripts, se necessário:

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

    Felicidades.

    Se você tiver pstree e perl no seu sistema, você pode tentar isto:

     perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)' 

    Se você sabe o pid da coisa que você quer matar, você normalmente pode ir do id da session, e tudo na mesma session. Eu verifiquei novamente, mas eu usei isso para scripts iniciando rsyncs em loops que eu quero morrer, e não iniciar outro (por causa do loop) como se eu tivesse acabado de matar o rsync.

     kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709)) 

    Se você não conhece o pid você ainda pode aninhar mais

     kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync ))) 
     ps -o pid= --ppid $PPID | xargs kill -9 

    No sh, o comando jobs listará os processos em segundo plano. Em alguns casos, pode ser melhor matar o processo mais recente primeiro, por exemplo, o mais antigo criou um soquete compartilhado. Nesses casos, classifique os PIDs em ordem inversa. Às vezes você quer esperar o momento para que os trabalhos escrevam algo no disco ou coisas assim antes que parem.

    E não mate se você não precisa!

     for SIGNAL in TERM KILL; do for CHILD in $(jobs -s|sort -r); do kill -s $SIGNAL $CHILD sleep $MOMENT done done 

    Matando o processo filho no shell script:

    Muitas vezes precisamos matar o processo filho que é enforcado ou bloqueado por algum motivo. por exemplo. Problema de conexão FTP.

    Existem duas abordagens

    1) Criar um novo pai separado para cada filho que irá monitorar e matar o processo filho quando o tempo limite for atingido.

    Crie test.sh como segue,

     #!/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; 

    e observe processos que estão tendo nome como ‘teste’ em outro terminal usando o seguinte comando.

     watch -n1 'ps x -o "%p %r %c" | grep "test" ' 

    O script acima criará 4 novos processos filhos e seus pais. Cada processo filho será executado por 10 segundos. Mas quando o tempo limite de 5 segundos atingir, os respectivos processos pai matarão esses filhos. Então, o filho não poderá concluir a execução (10 segundos). Jogar em torno desses horários (switch 10 e 5) para ver outro comportamento. Nesse caso, o filho concluirá a execução em 5 segundos antes de atingir o tempo limite de 10 segundos.

    2) Deixe o pai atual monitorar e matar o processo filho quando o tempo limite for atingido. Isso não criará um pai separado para monitorar cada filho. Além disso, você pode gerenciar todos os processos filhos adequadamente dentro do mesmo pai.

    Crie test.sh como segue,

     #!/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; 

    e observe processos que estão tendo nome como ‘teste’ em outro terminal usando o seguinte comando.

     watch -n1 'ps x -o "%p %r %c" | grep "test" ' 

    O script acima criará 4 novos processos filhos. Estamos armazenando pids de todo o processo filho e fazendo um loop sobre eles para verificar se eles terminaram sua execução ou ainda estão em execução. O processo filho será executado até o horário CMD_TIME. Mas se o tempo limite de CNT_TIME_OUT atingir, todos os filhos serão mortos pelo processo pai. Você pode alternar o tempo e brincar com o script para ver o comportamento. Uma desvantagem dessa abordagem é usar ID de grupo para matar toda a tree filho. Mas o processo pai em si pertence ao mesmo grupo e também será morto.

    Pode ser necessário atribuir outro id de grupo ao processo pai, se você não quiser que o pai seja morto.

    Mais detalhes podem ser encontrados aqui,

    Matando processo filho no shell script

    Este script também funciona:

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