Eu quero matar uma árvore de processo inteira. Qual é a melhor maneira de fazer isso usando qualquer linguagem de script comum? Estou procurando uma solução simples.
chronos
ou herodes
.
Eu quero matar uma árvore de processo inteira. Qual é a melhor maneira de fazer isso usando qualquer linguagem de script comum? Estou procurando uma solução simples.
chronos
ou herodes
.
Respostas:
Você não diz se a árvore que deseja matar é um único grupo de processos. (Geralmente, esse é o caso se a árvore for o resultado da bifurcação a partir de uma inicialização do servidor ou de uma linha de comando do shell.) Você pode descobrir grupos de processos usando o GNU ps da seguinte maneira:
ps x -o "%p %r %y %x %c "
Se você deseja matar um grupo de processos, basta usar o kill(1)
comando, mas em vez de atribuir um número de processo, negue o número do grupo. Por exemplo, para matar todos os processos do grupo 5112, use kill -TERM -- -5112
.
pgrep
pode oferecer uma maneira mais fácil de encontrar o ID do grupo de processos. Por exemplo, para matar o grupo de processos do my-script.sh, execute 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
Mate todos os processos pertencentes à mesma árvore de processos usando o ID do grupo de processos ( PGID
)
kill -- -$PGID
Use sinal padrão ( TERM
= 15)kill -9 -$PGID
Use o sinal KILL
(9)Você pode recuperar a PGID
partir de qualquer Process-ID ( PID
) da mesma árvore 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 ao tanager e Speakus pelas contribuições nos $PID
espaços restantes e compatibilidade com OSX.
kill -9 -"$PGID"
=> Envie o sinal 9 ( KILL
) para todos os filhos e netos ...PGID=$(ps opgid= "$PID")
=> Recupere o ID do grupo de processos de qualquer ID de processo da árvore, não apenas o ID dos pais do processo . Uma variação de ps opgid= $PID
é ps -o pgid --no-headers $PID
onde pgid
pode ser substituída por pgrp
. grep -o [0-9]*
imprime apenas dígitos sucessivos (não imprime espaços ou cabeçalhos alfabéticos).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
é invocado por um processo pertencente à mesma árvore, kill
corre o risco de se matar antes de terminar a matança inteira.> 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)"
Execute a árvore de processos 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
Percebo neste exemplo PID
e PGID
são iguais ( 28957
).
É por isso que originalmente pensei que kill -- -$PID
era suficiente. Mas, no caso em que o processo é gerado em um IDMakefile
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 uma árvore de processo inteira quando chamada de uma ID de grupo diferente (outra árvore de processo).
kill
deve sempre enviar o sinal para toda a árvore antes de receber seu próprio sinal. Mas, em algumas circunstâncias / implementações específicas, kill
pode enviar para si o sinal, ser interrompido e, em seguida, receber seu próprio sinal. No entanto, o risco deve ser mínimo o suficiente e pode ser ignorado na maioria dos casos, porque outros erros devem ocorrer antes deste. Esse risco pode ser ignorado no seu caso? Além disso, outras respostas têm esse bug comum ( kill
parte da árvore do processo sendo morta). Espero que esta ajuda .. Cheers;)
man
essas fazem isso. Por outro lado, se você quiser matar o processo gandchild do processo filho, kill -- -$pid
não funcionará. Portanto, não é uma solução genérica.
child.sh
.
pkill -TERM -P 27888
Isso matará todos os processos que têm o ID do processo pai 27888.
Ou mais robusto:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
que planejam matar 33 segundos depois e pedem educadamente que os processos sejam encerrados.
Veja esta resposta para encerrar todos os descendentes.
pkill -P
envia o sinal apenas para o filho => o neto não recebe o sinal => Portanto, escrevi outra resposta para explicar isso. Cheers ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Para matar uma árvore de processo 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) <pid> [signal]"
exit 1
fi
killtree $@
--
argumentos para ps
não funcionar no OS X. Para fazê-lo funcionar, substitua o ps
comando por: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
where _regex
é definido antes do for
loop:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
funcione de maneira confiável?
ps
não suporta --ppid
, pode-se usar em pgrep -P ${_pid}
vez disso
A resposta de Brad é o que eu recomendaria também, exceto que você pode eliminar awk
completamente se usar a --ppid
opção 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 um pouco 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 dos pais.
Parece muito feio, mas funciona perfeitamente.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
a pstree
, assim que as linhas longas não se truncado; também pode ser mais simples de ler kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(não é necessário converter \n
para o espaço, pois funcionará bem), thx!
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
wait $pid
apenas em processos que você começou, nem todos os precessa, assim que esta não é uma solução genérica
wait
suprimirá a Terminated
mensagem se for uma criança.
Como não posso comentar (não há reputação suficiente), sou forçado a adicionar uma nova resposta , mesmo que essa não seja realmente uma resposta.
Há um pequeno problema com a resposta muito agradável e completa dada por @olibre em 28 de fevereiro. A saída de ps opgid= $PID
conterá espaços à esquerda para um PID menor que cinco dígitos porque ps
justifica a coluna (alinhar os números corretamente). 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 é tubulação ps
para tr
a espaços Remover:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Para adicionar à resposta de Norman Ramsey, pode valer a pena olhar para setsid se você deseja criar um grupo de processos.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
A função setsid () deve criar uma nova sessão, se o processo de chamada não for um líder de grupo de processos. Após o retorno, o processo de chamada será o líder da sessão desta nova sessão, o líder do grupo de processos de um novo grupo de processos e não terá terminal de controle. O ID do grupo de processos do processo de chamada deve ser definido 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 sessão.
Entendo que você pode criar um grupo a partir do processo inicial. Eu usei isso no php para poder matar uma árvore de processo inteira depois de iniciá-la.
Isso pode ser uma má ideia. Eu estaria interessado em comentários.
Inspirado pelo comentário de ysth
kill -- -PGID
em vez de atribuir um número de processo, negue o número do grupo. Como de costume em quase qualquer comando, se você deseja que um argumento normal que comece com
-
a não seja interpretado como uma opção, anteceda-o com--
PGID
a partir PID
. O que você acha? Cheers
A seguinte função shell é semelhante a muitas das outras respostas, mas funciona no Linux e no BSD (OS X, etc) sem dependências externas, como 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
dessa função para não perturbar outras variáveis (não locais) com o mesmo nome e garantir que o valor da variável local seja limpo após o término da função.
É super fácil fazer isso com python usando psutil . Basta instalar o psutil com o pip e 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()
Se você deseja 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. Ele não usa recursão 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 ocorre 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 nas quais você não pode confiar em poder gerenciar grupos de processos - como ao escrever scripts para testar um software que deve ser construído em vários ambientes - essa técnica de caminhar em árvores é definitivamente útil.
Obrigado pela sua sabedoria, pessoal. Meu script estava deixando alguns processos filhos na saída e a dica de negação tornou as coisas mais fáceis. Eu escrevi essa função 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.
Provavelmente é melhor matar os pais antes dos filhos; caso contrário, o pai pode gerar novos filhos novamente antes de ser morto. Estes vão sobreviver ao assassinato.
Minha versão do ps é diferente daquela acima; talvez muito velho, portanto o estranho grepping ...
Usar um script de shell em vez de uma função de shell tem muitas vantagens ...
No entanto, é basicamente ideia 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 e pai dos quais os filhos devem ser encerrados. O segundo argumento é um booleano para determinar se o pid pai também deve ser finalizado.
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
Isso enviará o SIGTERM a qualquer processo filho / neto em um script de shell e, se o SIGTERM não for bem-sucedido, ele aguardará 10 segundos e enviará kill.
Resposta anterior:
O seguinte também funciona, mas mata 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
Desenvolvo a solução de zhigang, xyuri e 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
Esta versão evitará matar sua ancestralidade - o que causa uma enxurrada de processos filhos nas soluções anteriores.
Os processos são interrompidos corretamente antes que a lista de filhos seja determinada, para que nenhum novo filho seja criado ou desapareça.
Após serem mortos, os trabalhos interrompidos precisam continuar para desaparecer do sistema.
Pergunta antiga, eu sei, mas todas as respostas parecem continuar chamando ps, das quais eu não gostei.
Essa solução baseada em awk não requer recursão e chama ps apenas 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 construamos uma matriz (a) de entradas pai: filho e, em seguida, percorra a matriz encontrando filhos para nossos pais correspondentes, adicionando-os à nossa lista de pais (p) à medida que avançamos.
Se você não deseja interromper o processo de nível superior, faça
sub(/[0-9]*/, "", p)
pouco antes da linha system () a removeria do conjunto de interrupções.
Lembre-se de que existe uma condição de corrida aqui, mas isso é verdade (até onde posso ver) de todas as soluções. Ele faz o que eu precisava, porque o script para o qual eu precisava não cria muitas crianças de vida curta.
Um exercício para o leitor seria transformá-lo em um loop de 2 passagens: após a primeira passagem, envie SIGSTOP para todos os processos na lista p, depois faça um loop para executar ps novamente e após a segunda passagem envie SIGTERM e depois SIGCONT. Se você não se importa com finais agradáveis, o segundo passe pode ser SIGKILL, suponho.
Se você conhece o detalhe da coisa que deseja matar, geralmente pode ir do ID da sessão e de tudo na mesma sessão. Eu checava duas vezes, mas usei isso para scripts que iniciam o rsyncs em loops que eu quero morrer, e não inicio outro (por causa do loop) como faria 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, 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
kill -9
Realmente não .
kill -15
não ajuda.
Em sh, o comando jobs listará os processos em segundo plano. Em alguns casos, pode ser melhor eliminar primeiro o processo mais recente, por exemplo, o mais antigo criou um soquete compartilhado. Nesses casos, classifique os PIDs na ordem inversa. Às vezes, você deseja esperar um momento para que os trabalhos gravem algo no disco ou coisas assim antes que parem.
E não mate se não precisar!
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 processos filhos que são pendurados ou bloqueados por algum motivo. por exemplo. Problema de conexão FTP.
Existem duas abordagens,
1) Criar novo pai separado para cada filho, que monitorará e interromperá o processo filho assim que o tempo limite for atingido.
Crie test.sh da seguinte maneira,
#!/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 os processos que estão tendo o nome como 'test' 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. Porém, quando o tempo limite de 5s atingir, seus respectivos processos pai matarão esses filhos. Portanto, o filho não poderá concluir a execução (10s). Brinque com esses horários (alterne 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 interromper 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 corretamente dentro do mesmo pai.
Crie test.sh da seguinte maneira,
#!/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 os processos que estão tendo o nome como 'test' 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 todos os processos filhos e fazendo um loop sobre eles para verificar se eles concluíram 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 mudar o tempo e brincar com o script para ver o comportamento. Uma desvantagem dessa abordagem é que ela está usando a identificação de grupo para eliminar todas as árvores filhas. Mas o processo pai pertence ao mesmo grupo e, portanto, também é morto.
Pode ser necessário atribuir outra identificação de grupo ao processo pai, se você não quiser que o pai seja morto.
Mais detalhes podem ser encontrados aqui,
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
Eu sei que é velho, 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
}