Bash executando vários programas e manipulando próximo

4

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 eu clico em Ctl-C para fechar o lastProgram ele sai e todos os outros continuam rodando em segundo plano. O problema é que os outros programas precisam ser finalizados.

Qual é a maneira correta de lidar com o fechamento de todos os programas iniciados a partir do script?

    
por Ricard Molins 28.04.2017 / 17:11

3 respostas

4

Colete os IDs do processo, mate 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

Trapping EXIT executa a função quando o shell sai, independentemente do motivo. Você pode alterar isso para trap killbg SIGINT para executá-lo somente em ^C .

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

Ou mate-os por id do trabalho. Vamos ler a saída 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 tarefa com set -m ("modo monitor") para que isso funcione. Caso contrário, apenas o processo de lead será encerrado.

    
por 28.04.2017 / 18:40
3

Eu estava lendo perguntas semelhantes sobre a coleta de PIDs e, em seguida, as matando no final de um script - o problema é que um PID para um processo final poderia ser reciclado e recuperado usado em um novo processo antes que seu script termine, e então você poderia matar um novo processo (aleatório) .

Arrebatar EXIT e matar com o controle de trabalho do bash

Você poderia usar o controle de tarefa do bash para apenas matar processos iniciados no script com uma interceptação 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

Qualquer extra "mata" para jobspec inexistentes que já tenham terminado apenas resulta em uma mensagem de erro, não matará nenhum outro processo novo / aleatório.

mate o grupo de processos completo do seu script

Aqui está uma maneira um pouco diferente de matar o grupo de processos completo do seu script. Mas se o controle de tarefa do script / shell não estiver configurado, ele poderia herdar o PPID do pai ... mas sem o controle do trabalho acima, isso também não funcionaria.

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

trap 'kill -- -$$' EXIT

Consulte este Q relacionado ou aqui onde Johannes 'fish' Ziemke intercepta SIGINT e SIGTERM e usa setsid para" mata o grupo de processos completo em um novo processo grupo, então não correremos o risco de matar nossos eus . ")

    
por 28.04.2017 / 19:04
0

Se você quiser matar todos os processos em segundo plano se eles não forem concluídos antes que lastProgram tenha terminado a 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 tenham terminado a execução antes de sair do script, você poderá usar o comando wait .

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait
    
por 28.04.2017 / 18:18