
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, lastProgram
salgo 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 EXIT
ejecuta la función cuando se cierra el shell, independientemente del motivo. Podrías cambiar eso para trap killbg SIGINT
que 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 jobs
para 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 setsid
para "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 lastProgram
termine 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 wait
comando.
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.