Respuestas:
Para limpiar un poco de desorden, trapse 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 traple 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 backgroundsupone que es un marcador de posición? backgroundno 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)-releaseOSX, 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 killo 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 INTy TERMsalir? Porque ambos deberían desencadenar elkill 0 sin entrar en un bucle infinito.
¿Por gatillo kill 0en 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 0significa / 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 EXITsolució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:
SIGINTo SIGTERMo EXIT;kill 0, lo que envía SIGTERMa 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; stopfunció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 0envía SIGTERM, que quedará atrapado una vez más. Sin embargo, esto no producirá una recursión infinita, ya SIGTERMque se eliminará durante la segunda stopllamada.
trap - $1 && kill -s $1 0debería funcionar mejor. Probaré y actualizaré esta respuesta. Gracias por la buena idea! :)
trap - $1 && kill -s $1 0tampoco funcionará, ya que no podemos matar EXIT. Pero es realmente suficiente destrabar TERM, porque killenvía esta señal por defecto.
EXIT, el trapcontrolador 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 -prestá 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 -prno 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_codeviene en la INT TERMtrampa?
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; doneSi 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 trapno 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