Cómo terminar árbol de procesos de escritura en Cygwin fiesta de escritura del golpe
Pregunta
Tengo un script bash Cygwin que necesito para ver y terminar bajo ciertas condiciones - en concreto, después de un determinado archivo se ha creado. Estoy teniendo dificultades para averiguar cómo exactamente para terminar el guión con el mismo nivel de integridad que Ctrl + C hace, sin embargo.
Aquí hay un script sencillo (llamado test1
) que no hace más que esperar para ser terminado.
#!/bin/bash
test -f kill_me && rm kill_me
touch kill_me
tail -f kill_me
Si este script se ejecuta en el primer plano, Ctrl + C terminará tanto el tail
y el propio guión. Si el script se ejecuta en segundo plano, una kill %1
(suponiendo que es el trabajo 1) también se resolverán tanto tail
y el guión.
Sin embargo, cuando trato de hacer lo mismo a partir de un guión, me estoy encontrando que sólo el proceso bash
ejecuta el script es terminado, mientras que cuelga alrededor tail
desconectado de su matriz. He aquí una manera Probé ( test2
):
#!/bin/bash
test -f kill_me && rm kill_me
(
touch kill_me
tail -f kill_me
) &
while true; do
sleep 1
test -f kill_me && {
kill %1
exit
}
done
Si esto se ejecuta, la fiesta subnivel ejecuta en segundo plano se termina bien, pero tail
todavía cuelga alrededor.
Si utilizo un script de forma explícita por separado, como este, todavía no funciona ( test3
):
#!/bin/bash
test -f kill_me && rm kill_me
# assuming test1 above is included in the same directory
./test1 &
while true; do
sleep 1
test -f kill_me && {
kill %1
exit
}
done
tail
todavía está dando vueltas después de este script se ejecuta.
En mi caso concreto, el proceso de creación de archivos no es particularmente instrumentable, así que no puedo conseguirlo para terminar por sí misma; averiguando cuando se ha creado un archivo en particular, sin embargo, puedo en ese momento saber que está bien que por terminado. Por desgracia, no puedo usar un killall
simple o equivalente, ya que puede haber varias instancias en ejecución, y yo sólo quiero matar a la instancia específica.
Solución
/ bin / kill (el programa, no la orden interna bash) interpreta un negativa PID como “matar al grupo de procesos” que le llevará a todos los niños también.
Cambio
kill %1
a
/bin/kill -- -$$
funciona para mí.
Otros consejos
Enlace de Adam me puso en una dirección que va a resolver el problema, aunque no sin algunas advertencias menores.
El script no funciona sin modificar bajo Cygwin, por lo que volvió a escribir, y con un par de opciones. Aquí está mi versión:
#!/bin/bash
function usage
{
echo "usage: $(basename $0) [-c] [-<sigspec>] <pid>..."
echo "Recursively kill the process tree(s) rooted by <pid>."
echo "Options:"
echo " -c Only kill children; don't kill root"
echo " <sigspec> Arbitrary argument to pass to kill, expected to be signal specification"
exit 1
}
kill_parent=1
sig_spec=-9
function do_kill # <pid>...
{
kill "$sig_spec" "$@"
}
function kill_children # pid
{
local target=$1
local pid=
local ppid=
local i
# Returns alternating ids: first is pid, second is parent
for i in $(ps -f | tail +2 | cut -b 10-24); do
if [ ! -n "$pid" ]; then
# first in pair
pid=$i
else
# second in pair
ppid=$i
(( ppid == target && pid != $$ )) && {
kill_children $pid
do_kill $pid
}
# reset pid for next pair
pid=
fi
done
}
test -n "$1" || usage
while [ -n "$1" ]; do
case "$1" in
-c)
kill_parent=0
;;
-*)
sig_spec="$1"
;;
*)
kill_children $1
(( kill_parent )) && do_kill $1
;;
esac
shift
done
El único inconveniente real es el mensaje algo feo que bash imprime cuando se recibe una señal fatal, a saber, "Terminado", "muertos" o "interrumpida" (dependiendo de lo que se envía). Sin embargo, puedo vivir con eso en secuencias de comandos por lotes.
Este script parece que 'll hacer el trabajo:
#!/bin/bash
# Author: Sunil Alankar
##
# recursive kill. kills the process tree down from the specified pid
#
# foreach child of pid, recursive call dokill
dokill() {
local pid=$1
local itsparent=""
local aprocess=""
local x=""
# next line is a single line
for x in `/bin/ps -f | sed -e '/UID/d;s/[a-zA-Z0-9_-]\{1,\}
\{1,\}\([0-9]\{1,\}\) \{1,\}\([0-9]\{1,\}\) .*/\1 \2/g'`
do
if [ "$aprocess" = "" ]; then
aprocess=$x
itsparent=""
continue
else
itsparent=$x
if [ "$itsparent" = "$pid" ]; then
dokill $aprocess
fi
aprocess=""
fi
done
echo "killing $1"
kill -9 $1 > /dev/null 2>&1
}
case $# in
1) PID=$1
;;
*) echo "usage: rekill <top pid to kill>";
exit 1;
;;
esac
dokill $PID