Estoy buscando una manera de limpiar el desorden cuando sale mi script de nivel superior.
Especialmente si quiero usar set -e
, desearía que el proceso en segundo plano muriera cuando salga el script.
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):
kill -- -$$
envía una SIGTERM a todo el grupo de procesos, matando así también a los descendientes.Especificar la señal
EXIT
es útil cuando se usaset -e
(más detalles aquí ).fuente
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 enman 1 kill
oman 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.
¿Por qué convertir
INT
yTERM
salir? Porque ambos deberían desencadenar elkill 0
sin entrar en un bucle infinito.¿Por gatillo
kill 0
enEXIT
? 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 .fuente
kill 0
significa / hace exactamente ?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:
fuente
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
oSIGTERM
oEXIT
;kill 0
, lo que envíaSIGTERM
a todos los procesos del grupo, incluido el propio shell;Esto se puede solucionar eliminando manualmente el registro de la trampa:
La forma más elegante, que permite imprimir la señal recibida y evita los mensajes "Terminado:":
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!fuente
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 lastop()
función (hacerlo podría detener otras señales recursivas (que no sean las 3 codificadas)).SIGINT
, perokill 0
envíaSIGTERM
, que quedará atrapado una vez más. Sin embargo, esto no producirá una recursión infinita, yaSIGTERM
que se eliminará durante la segundastop
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 matarEXIT
. Pero es realmente suficiente destrabarTERM
, porquekill
envía esta señal por defecto.EXIT
, eltrap
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:
o evitando la función por completo:
¿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):porque
jobs -pr
está vacío: terminé en esa 'trampa' (juego de palabras).fuente
[ -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.
Tenga en cuenta que los trabajos no incluyen procesos de nietos.
fuente
Como https://stackoverflow.com/a/22644006/10082476 , pero con código de salida agregado
fuente
exit_code
viene en laINT TERM
trampa?Otra opción es que el script se establezca como el líder del grupo de procesos y atrape un killpg en su grupo de procesos al salir.
fuente
Entonces script la carga del script. Ejecute un
killall
comando (o lo que esté disponible en su sistema operativo) que se ejecute tan pronto como finalice el script.fuente
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:
La llamada a "trabajos" es necesaria con el shell dash de Debian, que no puede actualizar el trabajo actual ("%%") si falta.
fuente
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&
):Ejemplo de trabajo:
fuente
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:
fuente