Armadilha EXIT e kill com o controle de trabalho do bash

Armadilha EXIT e kill com o controle de trabalho do bash

Eu tenho um script bash que executa vários programas

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

Eu corro como./myscript.sh

Quando clico em Ctrl+ Cpara fechar ele lastProgramsai e todos os outros continuam rodando em segundo plano. O problema é que os outros programas precisam ser encerrados.

Qual a maneira correta de fechar todos os programas iniciados a partir do script?

Responder1

Colete os IDs do processo e elimine os processos em segundo plano ao sair.

#!/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

O trapping EXITexecuta a função quando o shell é encerrado, independentemente do motivo. Você pode alterar isso para trap killbg SIGINTexecutá-lo apenas no ^C.

Isso não verifica se um dos processos em segundo plano foi encerrado antes que o script tentasse dispara-los. Se o fizerem, você poderá obter erros ou, pior, disparar no processo errado.


Ou mate-os pelo ID do trabalho. Vamos ler o resultado de jobspara descobrir quais ainda estão ativos.

#!/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

Se você executar processos em segundo plano que geram outros processos (como um subshell: (sleep 1234 ; echo foo) &), será necessário ativar o controle de trabalho com set -m("modo monitor") para que isso funcione. Caso contrário, apenas o processo principal será encerrado.

Responder2

Eu estava lendo perguntas semelhantes sobre coletar PIDs e depois matá-los todos no final de um script - oproblemaé que um PID para um processo finalizadopoderia ser reciclado e reutilizado em um novo processoantes que seu script termine, e então você poderiamatar um novo processo (aleatório).

Armadilha EXIT e kill com o controle de trabalho do bash

Você poderia usar o controle de trabalho do bash para eliminar apenas processos iniciados no script com um trap e %n jobspec, contando o número máximo de trabalhos que poderiam estar em execução (apenas 3 neste exemplo):

#!/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

Quaisquer "eliminações" extras de especificações de trabalho inexistentes que já foram concluídas apenas resultam em uma mensagem de erro, não eliminarão nenhum outro processo novo/aleatório.


mate o grupo de processos completo do seu script

Aqui está uma maneira um pouco diferente de eliminar o grupo completo de processos do seu script. Mas se o controle de trabalho do seu script/shell não estiver configurado, entãopoderiaherdar o PPID do pai ... mas sem o controle do trabalho o procedimento acima também não funcionaria.

A diferença é este comando kill para trap, usando o PID do bash, já que ele se torna o PGID para novos processos:

trap 'kill -- -$$' EXIT

Vereste Q relacionadoouaqui onde Johannes 'pesca' Ziemke prende SIGINT e SIGTERM e usa setsidpara "matar o grupo de processos completo em um novo grupo de processos para não corrermos o risco de nos matar.")

Responder3

Se você quiser encerrar todos os processos em segundo plano se eles não forem concluídos antes lastProgramda execução, você precisará armazenar todos os pids:

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

kill $p1pid $p2pid $p3pid

Se você simplesmente quiser esperar até que todos os processos em segundo plano terminem de ser executados antes de sair do script, você pode usar o waitcomando.

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

Responder4

Aqui está meu comando de uma linha (in bash):

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

O que ele faz é capturar Ctrl+ Cpara eliminar todos os trabalhos em segundo plano e voltar ao comportamento padrão.

Em seguida, ele executa todos os comandos em segundo plano.

Quando você pressiona Ctrl+ C, ele mata todos eles e redefine o trap de volta ao seu comportamento padrão.

informação relacionada