Trampa EXIT y mata con el control de trabajo de bash

Trampa EXIT y mata con el control de trabajo de bash

Tengo un script bash que ejecuta múltiples programas.

#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram

lo ejecuto como./myscript.sh

Cuando presiono Ctrl+ Cpara cerrarlo, lastProgramsalgo y todos los demás siguen ejecutándose en segundo plano. El problema es que es necesario cerrar los demás programas.

¿Cuál es la forma correcta de manejar el cierre de todos los programas iniciados desde el script?

Respuesta1

Recopile las ID del proceso, elimine los procesos en segundo plano al salir.

#!/bin/bash
killbg() {
        for p in "${pids[@]}" ; do
                kill "$p";
        done
}
trap killbg EXIT
pids=()
background job 1 & 
pids+=($!)
background job 2... & 
pids+=($!)
foreground job

La captura EXITejecuta la función cuando se cierra el shell, independientemente del motivo. Podrías cambiar eso para trap killbg SIGINTque solo se ejecute en ^C.

Esto no comprueba si uno de los procesos en segundo plano salió antes de que el script intentara ejecutarlos. Si lo hacen, podría obtener errores o, peor aún, ejecutar el proceso incorrecto.


O matarlos por identificación de trabajo. Leamos el resultado de jobspara saber cuáles siguen activos.

#!/bin/bash 
killjobs() {
    for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do 
        kill %$x
    done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30

Si ejecuta procesos en segundo plano que generan otros procesos (como un subshell:) (sleep 1234 ; echo foo) &, debe habilitar el control de trabajos con set -m("modo monitor") para que esto funcione. De lo contrario, simplemente se finaliza el proceso principal.

Respuesta2

Estaba leyendo preguntas similares sobre cómo recopilar PID y luego eliminarlos todos al final de un script: elproblema¿Es ese un PID para un proceso terminado?podría reciclarse y reutilizarse en un nuevo procesoantes de que termine su guión, y luego podríamatar un nuevo proceso (aleatorio).

Trampa EXIT y mata con el control de trabajo de bash

Podrías usar el control de trabajos de bash para finalizar solo los procesos iniciados en el script con una trampa y %n especificaciones de trabajo, contando el número máximo de trabajos que podrían estar ejecutándose (solo 3 en este ejemplo):

#!/bin/bash
#trap 'kill %1 %2 %3' 0     # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT  # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed

Cualquier "eliminación" adicional de especificaciones de trabajo inexistentes que ya hayan finalizado solo genera un mensaje de error, no eliminará ningún otro proceso nuevo/aleatorio.


mata el grupo de procesos completo de tu script

Aquí hay una forma ligeramente diferente de eliminar el grupo de procesos completo de su script. Pero si el control de trabajo de su script/shell no está configurado, entoncespodríaherede su PPID de su padre... pero sin control de trabajo lo anterior tampoco funcionaría.

La diferencia es este comando kill para trampa, que usa el PID de bash, ya que se convierte en el PGID para nuevos procesos:

trap 'kill -- -$$' EXIT

Veresta Q relacionadaoaquí donde Johannes 'pez' Ziemke atrapa SIGINT y SIGTERM y los usa setsidpara "elimine el grupo de procesos completo en un nuevo grupo de procesos para que no corramos el riesgo de suicidarnos".)

Respuesta3

Si desea eliminar todos los procesos en segundo plano si no se completan antes de que lastProgramtermine de ejecutarse, deberá almacenar todos los pids:

python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram

kill $p1pid $p2pid $p3pid

Si simplemente desea esperar hasta que todos los procesos en segundo plano hayan terminado de ejecutarse antes de salir del script, puede usar el waitcomando.

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait

Respuesta4

Aquí está mi comando de una línea (en bash):

trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command

Lo que hace es atrapar Ctrl+ Cpara eliminar todos los trabajos en segundo plano y volver a su comportamiento predeterminado.

Luego, ejecuta todos los comandos en segundo plano.

Cuando presionas Ctrl+ C, los matará a todos y restablecerá la trampa a su comportamiento predeterminado.

información relacionada