usuarios - useradd linux ejemplos
¿Cuál es la mejor manera de enviar una señal a todos los miembros de un grupo de proceso? (30)
Aquí hay una variación de la respuesta de @zhigang que no tiene AWK, confiando solo en las posibilidades de análisis nativas 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 se escriben scripts para probar una pieza de software que debe estar integrada en múltiples entornos, esta técnica de caminar por el árbol es definitivamente útil.
Quiero matar todo un árbol de proceso. ¿Cuál es la mejor manera de hacerlo utilizando cualquier lenguaje de scripting común? Estoy buscando una solución simple.
Basado en la respuesta de zhigang, esto evita la matanza:
init_killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
init_killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
Desarrollé la solución de zhigang, xyuri y solidsneck más:
#!/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 su ascendencia, lo que provoca una avalancha de procesos secundarios en las soluciones anteriores.
Los procesos se detienen correctamente antes de que se determine la lista secundaria, de modo que no se creen ni desaparezcan nuevos hijos.
Después de ser eliminados, los trabajos detenidos deben continuar desapareciendo del sistema.
Elimine todos los procesos que pertenecen al mismo árbol de procesos utilizando la ID de grupo de procesos ( PGID
)
-
kill -- -$PGID
Usa la señal por defecto (TERM
= 15) -
kill -9 -$PGID
Usa la señalKILL
(9)
Puede recuperar el PGID
de cualquier ID de proceso ( PID
) del mismo árbol de proceso
-
kill -- -$(ps -o pgid= $PID | grep -o ''[0-9]*'')
(TERM
señal) -
kill -9 -$(ps -o pgid= $PID | grep -o ''[0-9]*'')
(señalKILL
)
Un agradecimiento especial a tanager y Speakus por las contribuciones en los espacios restantes de $PID
y la compatibilidad con OSX.
Explicación
-
kill -9 -"$PGID"
=> Envíe la 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 deps opgid= $PID
esps -o pgid --no-headers $PID
dondepgid
puede reemplazarse porpgrp
.
Pero:-
ps
inserta espaciosPID
cuandoPID
tiene menos de cinco dígitos y está alineado a la derecha según lo notado por tanager . Puedes usar:
PGID=$(ps opgid= "$PID" | tr -d '' '')
-
ps
desde OSX siempre imprime el encabezado, por Speakus tanto, Speakus propone:
PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d '' '' )"
-
-
grep -o [0-9]*
imprime solo dígitos sucesivos (no imprime espacios ni encabezados alfabéticos).
Lineas de comando adicionales
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)
Limitación
- Como lo notaron davide y Hubert Kario , cuando se invoca a
kill
por un proceso que pertenece al mismo árbol,kill
riesgos de suicidarse antes de terminar con la matanza del árbol completo. - Por lo tanto, asegúrese de ejecutar el comando utilizando un proceso que tenga un ID de grupo de procesos diferente.
Larga historia
> 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
Conclusión
PGID
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 genere dentro de un Makefile
la ID del proceso es diferente de la ID del 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).
En sh, el comando de trabajos listará 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, ordene los PID en orden inverso. A veces desea 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
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()
Esta es mi versión de matar todos los procesos secundarios utilizando el script bash. No utiliza recursión 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//,/ }
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
Gracias por su sabiduría, amigos. Mi script estaba dejando algunos procesos secundarios al salir y la sugerencia de negación facilitó las cosas. Escribí esta función para ser usada 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
}
Aclamaciones.
Inspirado por el comentario de ysth.
kill -- -PGID
en lugar de darle un número de proceso, denle la negación del número de grupo. Como es habitual con casi cualquier comando, si desea que un argumento normal que comience con un
-
para que no se interprete como un interruptor, debe precederlo con--
La respuesta de brad es lo que recomendaría también, excepto que puedes eliminar awk
completo si usas la opción --ppid
para ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
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
}
Lo siguiente ha sido probado en FreeBSD, Linux y MacOS X y solo depende de pgrep y kill (las versiones ps -o no funcionan en BSD). El primer argumento es el pid principal de los cuales los hijos deben ser terminados. El segundo argumento es un valor booleano para determinar si el pid principal debe terminarse también.
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á al 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
Matar proceso hijo en shell script:
Muchas veces necesitamos matar el proceso hijo que está colgado o bloqueado por alguna razón. p.ej. Problema de conexión FTP.
Hay dos enfoques,
1) Para crear un nuevo padre separado para cada hijo que monitoreará y eliminará el proceso hijo una vez que se alcance el tiempo de espera.
Crear 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 otra 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 hijo se ejecutará durante 10 s. Pero una vez que se agote el tiempo de espera de 5 segundos, sus procesos padres respectivos matarán a esos niños. Así que el niño no podrá completar la ejecución (10 s). Juegue alrededor de esos tiempos (interruptor 10 y 5) para ver otro comportamiento. En ese caso, el niño terminará la ejecución en 5 segundos antes de que llegue a un tiempo de espera de 10 segundos.
2) Deje que el padre actual supervise y mate el proceso una vez que se alcance el tiempo de espera. Esto no creará padres separados para monitorear a cada niño. También puede administrar todos los procesos secundarios correctamente dentro del mismo padre.
Crear 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 otra 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 los pids de todos los procesos secundarios y repasándolos en bucle para verificar si han terminado su ejecución o si todavía se están ejecutando. El proceso hijo se ejecutará hasta el tiempo CMD_TIME. Pero si CNT_TIME_OUT alcanza el tiempo de espera, todos los niños serán asesinados por el proceso principal. Puedes cambiar el tiempo y jugar con el script para ver el comportamiento. Un inconveniente de este enfoque es que utiliza la identificación de grupo para matar a todos los árboles secundarios. Pero el proceso de los padres pertenece al mismo grupo, por lo que también será asesinado.
Es posible que deba asignar otra ID de grupo al proceso principal si no desea que se elimine la función principal.
Más detalles se pueden encontrar aquí,
No dices si el árbol que quieres matar es un solo grupo de procesos. (Este suele ser el caso si el árbol es el resultado de forking desde un inicio de servidor o una línea de comando de shell). Puede descubrir grupos de procesos usando GNU ps de la siguiente manera:
ps x -o "%p %r %y %x %c "
Si desea eliminar un grupo de procesos, solo use el comando kill(1)
pero en lugar de darle un número de proceso, niegue el número de grupo. Por ejemplo, para eliminar todos los procesos del grupo 5112, use kill -TERM -- -5112
.
No puedo comentar (no tengo 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 muy agradable y completa de @olibre el 28 de febrero. La salida de ps opgid= $PID
contendrá espacios ps opgid= $PID
para un PID más corto que cinco dígitos porque ps
está justificando la columna (alinear correctamente números). Dentro de la línea de comando completa, esto resulta en un signo negativo, seguido de espacio (s), seguido del PID de grupo. Una solución simple es canalizar ps
a tr
para eliminar espacios:
kill -- -$( ps opgid= $PID | tr -d '' '' )
Para agregar a la respuesta de Norman Ramsey, puede valer la pena ver en 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á terminal de control. El ID de grupo de proceso del proceso de llamada se establecerá igual al 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 entiendo es que puedes crear un grupo desde el proceso de inicio. Utilicé esto en PHP para poder matar todo un árbol de procesos después de iniciarlo.
Esto puede ser una mala idea Me interesaría hacer comentarios.
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 $@
Probablemente es mejor matar a los padres antes que a los niños; de lo contrario, es probable que el padre vuelva a engendrar 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 una 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
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
}
Si conoce el pid de lo que quiere matar, normalmente puede ir desde el ID de sesión y todo en la misma sesión. Lo comprobé nuevamente, pero utilicé esto para los scripts que inician rsyncs en los bucles en los que quiero morir, y no en otro (debido al bucle) como lo haría si solo quisiera hacer killall''d rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Si no conoces el pid todavía puedes anidar más.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
Si quieres matar un proceso por nombre:
killall -9 -g someprocessname
o
pgrep someprocessname | xargs pkill -9 -g
Si sabe cómo 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
Si tiene pstree y perl en su sistema, puede intentar esto:
perl -e ''kill 9, (`pstree -p PID` =~ m//((/d+)/)/sg)''
Utilizo un poco la versión modificada de un método descrito aquí: https://.com/a/5311362/563175
Así 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.
Parece bastante feo, pero funciona perfectamente.
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
Vieja pregunta, lo sé, pero todas las respuestas parecen seguir llamando ps, lo que no me gustó.
Esta solución basada en awk no requiere recursión y solo llama a 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 padres: entradas secundarias, luego recorramos la matriz encontrando hijos para nuestros padres correspondientes, agregándolos a nuestra lista de padres (p) a medida que avanzamos.
Si no quieres matar el proceso de nivel superior, hazlo
sub(/[0-9]*/, "", p)
justo antes de que la línea del sistema () la eliminara del conjunto de eliminación.
Tenga en cuenta que hay una condición de carrera aquí, pero eso es cierto (por lo que puedo ver) de todas las soluciones. Hace lo que necesitaba porque el guión para el que lo necesitaba no crea muchos niños de corta duración.
Un ejercicio para el lector sería convertirlo en un bucle de 2 pases: después de la primera pasada, envíe SIGSTOP a todos los procesos en la lista p, luego haga un bucle para ejecutar ps otra vez y después de la segunda pasada envíe SIGTERM, luego SIGCONT. Si no te importan los buenos finales, el segundo paso podría ser SIGKILL, supongo.
pkill -TERM -P 27888
Esto eliminará todos los procesos que tengan el ID de proceso primario 27888.
O más robusto:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
qué horario matan 33 segundos más tarde y cortésmente piden procesos para terminar.
Vea esta respuesta para terminar con todos los descendientes.
ps -o pid= --ppid $PPID | xargs kill -9