Quiero matar todo un árbol de procesos. ¿Cuál es la mejor manera de hacer esto usando cualquier lenguaje de script común? Estoy buscando una solución simple.
chronos
o herodes
.
Quiero matar todo un árbol de procesos. ¿Cuál es la mejor manera de hacer esto usando cualquier lenguaje de script común? Estoy buscando una solución simple.
chronos
o herodes
.
Respuestas:
No dice si el árbol que desea matar es un solo grupo de procesos. (Este suele ser el caso si el árbol es el resultado de una bifurcación desde el inicio de un servidor o una línea de comando de shell). Puede descubrir grupos de procesos utilizando GNU ps de la siguiente manera:
ps x -o "%p %r %y %x %c "
Si es un grupo de proceso que desea eliminar, simplemente use el kill(1)
comando pero en lugar de darle un número de proceso, dele la negación del número de grupo. Por ejemplo, para eliminar todos los procesos del grupo 5112, use kill -TERM -- -5112
.
pgrep
puede ofrecer una manera más fácil de encontrar la ID del grupo de procesos. Por ejemplo, para matar el grupo de procesos my-script.sh, ejecute 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
Elimine todos los procesos que pertenecen al mismo árbol de procesos utilizando el ID del grupo de procesos ( PGID
)
kill -- -$PGID
Usar señal predeterminada ( TERM
= 15)kill -9 -$PGID
Usa la señal KILL
(9)Puede recuperar el PGID
desde cualquier ID de proceso ( PID
) del mismo árbol de procesos
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(señal TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(señal KILL
)Un agradecimiento especial a tanager y Speakus por sus contribuciones en $PID
los espacios restantes y la compatibilidad con OSX.
kill -9 -"$PGID"
=> Enviar señal 9 ( KILL
) a todos los hijos y nietos ...PGID=$(ps opgid= "$PID")
=> Recupere la ID de grupo de proceso de cualquier ID de proceso del árbol, no solo la ID de padre de proceso . Una variación de ps opgid= $PID
es ps -o pgid --no-headers $PID
donde pgid
se puede reemplazar por pgrp
. grep -o [0-9]*
imprime solo dígitos sucesivos (no imprime espacios ni encabezados 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
es invocado por un proceso que pertenece al mismo árbol, kill
corre el riesgo de suicidarse antes de terminar con la destrucción del árbol completo.> 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)"
Ejecute el árbol de procesos en 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
El comando pkill -P $PID
no mata al nieto:
> 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
El comando kill -- -$PGID
mata todos los procesos, incluido el nieto.
> 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
Noto en este ejemplo PID
y PGID
son iguales ( 28957
).
Es por eso que originalmente pensé que kill -- -$PID
era suficiente. Pero en el caso de que el proceso se desovar dentro de un Makefile
el ID de proceso es diferente del ID de grupo .
Creo que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
es el mejor truco simple para matar un árbol de proceso completo cuando se llama desde una ID de grupo diferente (otro árbol de proceso).
kill
que siempre debería enviar la señal a todo el árbol antes de recibir su propia señal. Pero en algunas circunstancias / implementaciones específicas, kill
puede enviarse la señal a sí mismo, ser interrumpido y luego recibir su propia señal. Sin embargo, el riesgo debe ser lo suficientemente mínimo, y puede ignorarse en la mayoría de los casos porque deberían ocurrir otros errores antes de este. ¿Se puede ignorar este riesgo en su caso? Además, otras respuestas tienen este error común ( kill
parte del árbol de proceso que se está eliminando). Espero que esta ayuda .. Saludos;)
man
hacer eso. Por otro lado, si desea eliminar el proceso gandchild del proceso hijo, kill -- -$pid
no funcionará. Por lo tanto, no es una solución genérica.
child.sh
.
pkill -TERM -P 27888
Esto eliminará todos los procesos que tengan el ID de proceso padre 27888.
O más robusto:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
que programan la muerte 33 segundos después y cortésmente solicitan que finalicen los procesos.
Vea esta respuesta para terminar con todos los descendientes.
pkill -P
envía la señal solo al niño => el nieto no recibe la señal => Por lo tanto, he escrito otra respuesta para explicar eso. Saludos ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Para matar un árbol de proceso de forma recursiva, 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
no funcionar en OS X. Para hacerlo funcionar, reemplace el ps
comando por: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
donde _regex
se define antes del for
bucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
funcione de manera confiable?
ps
no es compatible --ppid
, se puede usar pgrep -P ${_pid}
en su lugar
La respuesta de Brad es lo que yo también recomendaría, excepto que puede eliminarlo por awk
completo si usa la --ppid
opción ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
si sabes pasar el pid del proceso padre, aquí hay un script de shell que debería 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
Utilizo una versión un poco modificada de un método descrito aquí: https://stackoverflow.com/a/5311362/563175
Entonces se ve así:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
donde 24901 es el PID de los padres.
Se ve bastante feo pero hace su trabajo perfectamente.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
a pstree
, para que las líneas largas no se trunqueen se puede hacer más fácil de leer también con kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(no es necesario convertir \n
al espacio ya que funcionará bien), gracias!
Versión modificada de la respuesta 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
solo puede en procesos que ha comenzado, no en todos los precesos, por lo que esta no es una solución genérica
wait
suprimirá el Terminated
mensaje si es un niño.
No puedo comentar (no hay suficiente reputación), por lo que me veo obligado a agregar una nueva respuesta , aunque esta no sea realmente una respuesta.
Hay un pequeño problema con la respuesta por lo demás muy agradable y exhaustiva dada por @olibre el 28 de febrero. La salida de ps opgid= $PID
contendrá espacios iniciales para un PID de menos de cinco dígitos porque ps
está justificando la columna (alinee los números). Dentro de la línea de comando completa, esto da como resultado un signo negativo, seguido de espacio (s), seguido del PID del grupo. Una solución simple es a la tubería ps
a tr
los espacios eliminar los siguientes:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Para agregar a la respuesta de Norman Ramsey, puede valer la pena mirar setsid si desea crear un grupo de procesos.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
La función setsid () creará una nueva sesión, si el proceso de llamada no es un líder de grupo de proceso. Al regresar, el proceso de llamada será el líder de la sesión de esta nueva sesión, será el líder del grupo de proceso de un nuevo grupo de proceso y no tendrá un terminal de control. La ID del grupo de proceso del proceso de llamada se establecerá igual a la ID de proceso del proceso de llamada. El proceso de llamada será el único proceso en el nuevo grupo de procesos y el único proceso en la nueva sesión.
Lo que quiero decir es que puedes crear un grupo desde el proceso de inicio. Usé esto en php para poder matar un árbol de proceso completo después de iniciarlo.
Esta puede ser una mala idea. Me interesarían los comentarios.
Inspirado por el comentario de ysth
kill -- -PGID
en lugar de darle un número de proceso, dele la negación del número de grupo. Como es habitual con casi cualquier comando, si desea que un argumento normal que comienza con
-
a no se interprete como un interruptor, preceda con--
PGID
a partir PID
. ¿Qué piensas? Saludos
La siguiente función de shell es similar a muchas de las otras respuestas, pero funciona tanto en Linux como en BSD (OS X, etc.) sin dependencias 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
esa función para no perturbar otras variables (no locales) con el mismo nombre y garantizar que el valor de la variable local se limpie después de que finalice la función.
Es muy fácil hacer esto con python usando psutil . Simplemente instale psutil con pip y luego tendrá un conjunto completo de herramientas de manipulación de procesos:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Si quieres matar un proceso por nombre:
killall -9 -g someprocessname
o
pgrep someprocessname | xargs pkill -9 -g
Esta es mi versión de matar todos los procesos secundarios usando el script bash. No usa recursividad y depende del comando pgrep.
Utilizar
killtree.sh PID SIGNAL
Contenido 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//,/ }
Aquí hay una variación de la respuesta de @ zhigang que funciona sin AWK, confiando solo en las posibilidades de análisis nativo 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"
}
Parece funcionar bien tanto en Mac como en Linux. En situaciones en las que no puede confiar en la capacidad de administrar grupos de procesos, como cuando escribe scripts para probar un software que debe construirse en múltiples entornos, esta técnica de caminar por los árboles es definitivamente útil.
Gracias por su sabiduría, amigos. Mi script dejaba algunos procesos secundarios al salir y el consejo de negación facilitó las cosas. Escribí esta función para usarla en otros scripts si es necesario:
# 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
}
Salud.
Probablemente sea mejor matar al padre antes que a los hijos; de lo contrario, es probable que el padre genere nuevos hijos antes de que él mismo se mate. Estos sobrevivirán a la matanza.
Mi versión de ps es diferente de la anterior; tal vez demasiado viejo, por lo tanto, el extraño grepping ...
Usar un script de shell en lugar de una función de shell tiene muchas ventajas ...
Sin embargo, es básicamente idea 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
Lo siguiente ha sido probado en FreeBSD, Linux y MacOS X y solo depende de pgrep y kill (las versiones ps -o no funcionan bajo BSD). El primer argumento es el padre pid de que los hijos tienen que ser terminados. El segundo argumento es un valor booleano para determinar si el pid padre también debe terminarse.
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
Esto enviará SIGTERM a cualquier proceso hijo / nieto dentro de un script de shell y si SIGTERM no tiene éxito, esperará 10 segundos y luego enviará kill.
Respuesta anterior:
Lo siguiente también funciona, pero matará el propio shell en 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
Desarrollo aún más la solución de zhigang, xyuri y 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 versión evitará matar a sus ancestros, lo que provoca una avalancha de procesos secundarios en las soluciones anteriores.
Los procesos se detienen correctamente antes de determinar la lista secundaria, para que no se creen o desaparezcan nuevos elementos secundarios.
Después de ser asesinados, los trabajos detenidos deben continuar para desaparecer del sistema.
Antigua pregunta, lo sé, pero todas las respuestas parecen seguir llamando ps, lo que no me gustó.
Esta solución basada en awk no requiere recursividad y solo llama ps una 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)
}'
O en una sola línea:
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)}'
Básicamente, la idea es que construyamos una matriz (a) de entradas padre: hijo, luego recorramos la matriz buscando hijos para nuestros padres coincidentes, agregándolos a nuestra lista de padres (p) a medida que avanzamos.
Si no quieres matar el proceso de nivel superior, entonces haz
sub(/[0-9]*/, "", p)
justo antes de que la línea system () lo elimine del conjunto de eliminación.
Tenga en cuenta que hay una condición de carrera aquí, pero eso es cierto (hasta donde puedo ver) de todas las soluciones. Hace lo que necesitaba porque el script para el que lo necesitaba no crea muchos niños de corta duración.
Un ejercicio para el lector sería convertirlo en un ciclo de 2 pasadas: después de la primera pasada, envíe SIGSTOP a todos los procesos en la lista p, luego realice un ciclo para ejecutar ps nuevamente y después de la segunda pasada envíe SIGTERM, luego SIGCONT. Si no te interesan los finales agradables, supongo que el segundo pase podría ser SIGKILL.
Si conoce el pid de lo que desea matar, generalmente puede ir desde la identificación de la sesión y todo en la misma sesión. Verificaría dos veces, pero usé esto para scripts que inician rsyncs en bucles que quiero morir, y no iniciar otro (debido al bucle) como lo haría si acabara de matar a rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Si no conoces el pid, aún puedes anidar más
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
, de verdad.
kill -15
no ayuda.
En sh, el comando jobs enumerará los procesos en segundo plano. En algunos casos, podría ser mejor eliminar primero el proceso más nuevo, por ejemplo, el anterior creó un socket compartido. En esos casos, clasifique los PID en orden inverso. A veces quieres esperar un momento para que los trabajos escriban algo en el disco o cosas así antes de que se detengan.
¡Y no mates si no tienes que hacerlo!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Proceso secundario de matar en script de shell:
Muchas veces necesitamos matar procesos secundarios que se cuelgan o bloquean por alguna razón. p.ej. Problema de conexión FTP.
Hay dos enfoques,
1) Crear un nuevo padre separado para cada hijo que supervisará y eliminará el proceso hijo una vez que se agote el tiempo de espera.
Cree test.sh de la siguiente manera,
#!/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;
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
El script anterior creará 4 nuevos procesos secundarios y sus padres. Cada proceso secundario se ejecutará durante 10 segundos. Pero una vez que se agota el tiempo de espera de 5 segundos, sus respectivos procesos principales matarán a esos niños. Por lo tanto, el niño no podrá completar la ejecución (10 segundos). Juega alrededor de esos tiempos (interruptor 10 y 5) para ver otro comportamiento. En ese caso, el hijo finalizará la ejecución en 5 segundos antes de que alcance el tiempo de espera de 10 segundos.
2) Deje que el padre actual supervise y elimine el proceso hijo una vez que se agote el tiempo de espera. Esto no creará un padre separado para monitorear a cada niño. También puede administrar todos los procesos secundarios correctamente dentro del mismo padre.
Cree test.sh de la siguiente manera,
#!/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;
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
El script anterior creará 4 nuevos procesos secundarios. Estamos almacenando pids de todos los procesos secundarios y recorriéndolos para verificar si han terminado su ejecución o si aún se están ejecutando. El proceso hijo se ejecutará hasta el momento CMD_TIME. Pero si CNT_TIME_OUT alcanza el tiempo de espera, todos los niños serán asesinados por el proceso principal. Puede cambiar el tiempo y jugar con el script para ver el comportamiento. Un inconveniente de este enfoque es que está usando la identificación de grupo para matar todos los árboles secundarios. Pero el proceso padre en sí mismo pertenece al mismo grupo, por lo que también será asesinado.
Es posible que deba asignar otra identificación de grupo al proceso principal si no desea que se elimine el elemento principal.
Más detalles se pueden encontrar aquí,
Este script también 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
Sé que es viejo, pero esa es la mejor solución que encontré:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}