
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 lastProgram
sai 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 EXIT
executa a função quando o shell é encerrado, independentemente do motivo. Você pode alterar isso para trap killbg SIGINT
executá-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 jobs
para 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 setsid
para "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 lastProgram
da 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 wait
comando.
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.