Respuestas:
Para limpiar un poco de desorden, trap
se puede utilizar. Puede proporcionar una lista de cosas ejecutadas cuando llega una señal específica:
trap "echo hello" SIGINT
pero también se puede usar para ejecutar algo si el shell sale:
trap "killall background" EXIT
Es un programa incorporado, por lo que help trap
le dará información (funciona con bash). Si solo desea eliminar trabajos en segundo plano, puede hacer
trap 'kill $(jobs -p)' EXIT
Tenga cuidado con el uso individual '
, para evitar que el caparazón sustituya al $()
inmediato.
kill $(jobs -p)
no funciona en el tablero, ya que la sustitución de comandos se ejecuta en un subnivel (ver comando de sustitución en el hombre guión)
killall background
supone que es un marcador de posición? background
no está en la página del manual ...
Esto funciona para mí (mejorado gracias a los comentaristas):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-release
OSX, y también se confirmó en Ubuntu . Sin embargo , hay un obvio obvio :)
-$$
. Se evalúa a '- <PID> `por ejemplo -1234
. En la página de manual de kill // página de manual integrada, un guión inicial especifica la señal que se enviará. Sin embargo, probablemente bloquea eso, pero el guión principal no está documentado de lo contrario. ¿Alguna ayuda?
man 2 kill
, lo que explica que cuando un PID es negativo, la señal se envía a todos los procesos en el grupo de procesos con la ID proporcionada ( en.wikipedia.org/wiki/Process_group ). Es confuso que esto no se mencione en man 1 kill
o man bash
, y podría considerarse un error en la documentación.
Actualización: https://stackoverflow.com/a/53714583/302079 mejora esto al agregar el estado de salida y una función de limpieza.
trap "exit" INT TERM
trap "kill 0" EXIT
¿Por qué convertir INT
y TERM
salir? Porque ambos deberían desencadenar elkill 0
sin entrar en un bucle infinito.
¿Por gatillo kill 0
en EXIT
? Debido a que las salidas de script normales deberían desencadenarsekill 0
.
¿Por qué kill 0
? Porque las subcapas anidadas también deben ser eliminadas. Esto eliminará todo el árbol de procesos .
kill 0
significa / hace exactamente ?
trampa 'kill $ (jobs -p)' EXIT
Solo haría cambios menores en la respuesta de Johannes y usaría jobs -pr para limitar la eliminación de los procesos en ejecución y agregar algunas señales más a la lista:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
La trap 'kill 0' SIGINT SIGTERM EXIT
solución descrita en la respuesta de @tokland es realmente agradable, pero el último Bash se bloquea con una falla de segmentación cuando se usa. Esto se debe a que Bash, a partir de la versión 4.3, permite la recursividad de trampa, que en este caso se vuelve infinita:
SIGINT
o SIGTERM
o EXIT
;kill 0
, lo que envía SIGTERM
a todos los procesos del grupo, incluido el propio shell;Esto se puede solucionar eliminando manualmente el registro de la trampa:
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
La forma más elegante, que permite imprimir la señal recibida y evita los mensajes "Terminado:":
#!/usr/bin/env bash
trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
local func="$1"; shift
for sig in "$@"; do
trap "$func $sig" "$sig"
done
}
stop() {
trap - SIGINT EXIT
printf '\n%s\n' "recieved $1, killing children"
kill -s SIGINT 0
}
trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &
while true; do read; done
UPD : ejemplo mínimo agregado; stop
función mejorada para evitar señales no necesarias y ocultar mensajes "Terminados:" de la salida. ¡Gracias Trevor Boyd Smith por las sugerencias!
stop()
usted proporciona el primer argumento como el número de señal pero luego codifica las señales que se están desregistrando. en lugar de codificar las señales que se están desregistrando, podría usar el primer argumento para anular el registro en la stop()
función (hacerlo podría detener otras señales recursivas (que no sean las 3 codificadas)).
SIGINT
, pero kill 0
envía SIGTERM
, que quedará atrapado una vez más. Sin embargo, esto no producirá una recursión infinita, ya SIGTERM
que se eliminará durante la segunda stop
llamada.
trap - $1 && kill -s $1 0
debería funcionar mejor. Probaré y actualizaré esta respuesta. Gracias por la buena idea! :)
trap - $1 && kill -s $1 0
tampoco funcionará, ya que no podemos matar EXIT
. Pero es realmente suficiente destrabar TERM
, porque kill
envía esta señal por defecto.
EXIT
, el trap
controlador de señal siempre se ejecuta solo una vez.
Para estar seguro, creo que es mejor definir una función de limpieza y llamarla desde la trampa:
cleanup() {
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]
o evitando la función por completo:
trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]
¿Por qué? Debido a que por el simple uso trap 'kill $(jobs -pr)' [...]
se supone que no va a ser trabajos en segundo plano que se ejecutan cuando se señaliza la condición trampa. Cuando no hay trabajos, verá el siguiente mensaje (o similar):
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
porque jobs -pr
está vacío: terminé en esa 'trampa' (juego de palabras).
[ -n "$(jobs -pr)" ]
no funciona en mi bash. Uso GNU bash, versión 4.2.46 (2) -release (x86_64-redhat-linux-gnu). El mensaje "kill: use" sigue apareciendo.
jobs -pr
no devuelve los PID de los hijos de los procesos en segundo plano. No destruye todo el árbol de procesos, solo corta las raíces.
Una buena versión que funciona bajo Linux, BSD y MacOS X. Primero intenta enviar SIGTERM, y si no tiene éxito, mata el proceso después de 10 segundos.
KillJobs() {
for job in $(jobs -p); do
kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)
done
}
TrapQuit() {
# Whatever you need to clean here
KillJobs
}
trap TrapQuit EXIT
Tenga en cuenta que los trabajos no incluyen procesos de nietos.
function cleanup_func {
sleep 0.5
echo cleanup
}
trap "exit \$exit_code" INT TERM
trap "exit_code=\$?; cleanup_func; kill 0" EXIT
# exit 1
# exit 0
Como https://stackoverflow.com/a/22644006/10082476 , pero con código de salida agregado
exit_code
viene en la INT TERM
trampa?
jobs -p no funciona en todos los shells si se llama en un sub-shell, posiblemente a menos que su salida se redirija a un archivo pero no a una tubería. (Supongo que originalmente estaba destinado solo para uso interactivo).
¿Qué pasa con lo siguiente:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
La llamada a "trabajos" es necesaria con el shell dash de Debian, que no puede actualizar el trabajo actual ("%%") si falta.
trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; done
Si lo ejecuta en Bash (5.0.3) e intenta terminar, parece que hay un bucle infinito. Sin embargo, si lo termina de nuevo, funciona. Incluso con Dash (0.5.10.2-6) tienes que terminarlo dos veces.
Hice una adaptación de la respuesta de @tokland combinada con el conocimiento de http://veithen.github.io/2014/11/16/sigterm-propagation.html cuando noté que eso trap
no se dispara si estoy ejecutando un proceso en primer plano (sin antecedentes &
):
#!/bin/bash
# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT
echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID
Ejemplo de trabajo:
$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1] + 31568 suspended bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31568 0.0 0.0 19640 1440 pts/18 T 01:30 0:00 bash killable-shell.sh sleep 100
niklas 31569 0.0 0.0 14404 616 pts/18 T 01:30 0:00 sleep 100
niklas 31605 0.0 0.0 18956 936 pts/18 S+ 01:30 0:00 grep --color=auto sleep
$ bg
[1] + 31568 continued bash killable-shell.sh sleep 100
$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1] + 31568 terminated bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31717 0.0 0.0 18956 936 pts/18 S+ 01:31 0:00 grep --color=auto sleep
Solo por diversidad, publicaré una variación de https://stackoverflow.com/a/2173421/102484 , porque esa solución lleva al mensaje "Terminado" en mi entorno:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT