ver segundo script procesos proceso plano enviar ejecutar comandos activos shell

enviar - ¿Cómo elimino procesos/trabajos en segundo plano cuando sale mi script de shell?



ver procesos activos unix (11)

trap ''kill $ (jobs -p)'' EXIT

Haría solo pequeños cambios en la respuesta y uso de trabajos de Johannes -pr para limitar los procesos kill a ejecutar y agregar unas cuantas señales más a la lista:

trap ''kill $(jobs -pr)'' SIGINT SIGTERM EXIT

Estoy buscando una manera de limpiar el desorden cuando sale mi guión de nivel superior.

Especialmente si quiero usar set -e , deseo que el proceso de fondo muera cuando la secuencia de comandos se cierre.


Así que script la carga del script. Ejecute un comando killall (o lo que esté disponible en su sistema operativo) que se ejecute tan pronto como finalice el script.


Esto funciona para mí (mejorado gracias a los comentaristas):

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT

  • kill -- -$$ envía un SIGTERM a todo el grupo de procesos, matando también a los descendientes.

  • La especificación de la señal EXIT es útil cuando se utiliza el set -e (más detalles here ).


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 la trap no se dispara si estoy ejecutando un primer plano proceso (no se basa en & ):

#!/bin/bash # killable-shell.sh: Kills itself and all children (the whole process group) when killed. # Adapted from http://.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 que funciona:

$ 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


La trap ''kill 0'' SIGINT SIGTERM EXIT solución descrita en la respuesta de @ tokland es realmente agradable, pero la última versión de Bash falla con un error de segmentación al usarla. Eso es porque Bash, a partir de la v. 4.3, permite la repetición de la trampa, que en este caso se vuelve infinita:

  1. el proceso de shell recibe SIGINT o SIGTERM o EXIT ;
  2. la señal queda atrapada, ejecutando kill 0 , que envía SIGTERM a todos los procesos del grupo, incluido el propio shell;
  3. ir a 1 :)

Esto puede solucionarse anulando manualmente la trampa:

trap ''trap - SIGTERM && kill 0'' SIGINT SIGTERM EXIT

La manera más elegante, que permite imprimir la señal recibida y evita los mensajes "Terminated:":

#!/usr/bin/env bash trap_with_arg() { # from https://.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; función de stop mejorada para evitar las señales innecesarias y ocultar los mensajes "finalizados:" de la salida. ¡Gracias Trevor Boyd Smith por las sugerencias!


Otra opción es que la secuencia de comandos se establezca como líder del grupo de procesos y atrape un killpg en su grupo de procesos al salir.


Para estar seguro, me parece mejor definir una función de limpieza y llamarla desde una 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é? Porque simplemente usando trap ''kill $(jobs -pr)'' [...] uno supone que habrá trabajos en segundo plano en ejecución cuando se señalice la condición de trampa. Cuando no hay trabajos, uno 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 intencionado).


Para limpiar un poco de basura, se puede usar trap . 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 built-in, así que help trap te dará información (funciona con bash). Si solo quiere matar trabajos en segundo plano, puede hacer

trap ''kill $(jobs -p)'' EXIT

Tenga cuidado con el uso de single '' , para evitar que el shell sustituya el $() inmediatamente.


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 los procesos de Grand Children.


trabajos -p no funciona en todos los shells si se llama en un sub-shell, posiblemente a menos que su salida sea redirigida a un archivo pero no a un pipe. (Supongo que originalmente estaba destinado solo para uso interactivo).

¿Qué hay de lo siguiente?

trap ''while kill %% 2>/dev/null; do jobs > /dev/null; done'' INT TERM EXIT [...]

La llamada a "trabajos" es necesaria con el tablero de instrumentos de Debian, que no puede actualizar el trabajo actual ("%%") si falta.


trap "exit" INT TERM trap "kill 0" EXIT

¿Por qué convertir INT y TERM para salir? Porque ambos deberían desencadenar kill 0 sin ingresar un bucle infinito.

¿Por qué desencadenar kill 0 en EXIT ? Debido a que las salidas de script normales también deberían disparar kill 0 .

¿Por qué kill 0 ? Debido a que las subcapas anidadas también deben ser eliminadas. Esto eliminará todo el árbol de procesos .