
Outro dia tentei escrever um roteiro que mataria umPIDe todos os seus processos filhos, mas depois de passar algum tempo nisso, decidi que não era confiável, porque às vezes alguns dos filhos acabavam com umPPIDde1.
Agora, o que estou procurando é como posso executar a seguinte função em segundo plano e ter a função e curl
tudo no mesmogrupo, e depois mate este script se ele travar, matando todos os seus filhos que foram deixados em segundo plano:
download(){
until curl -s -S -L -A Mozilla/5.0 -m 300 "$@"; do
echo Retrying in 5 seconds... >&2
sleep 5
done
}
for url in foo.com bar.com baz.com; do
download $url >$url &
done
wait
Eu sei que não há necessidade dessa função download
, mas incluí aqui porque é uma função que utilizo muitas vezes neste script. Além disso, estou dando um limite máximo de tempo de300para curl
, mas dependendo de alguns erros de rede, ele também trava.
Responder1
Normalmente, se você invocar seu script a partir de um shell interativo, ele será colocado em um novo grupo de processos (também conhecido como job
), portanto, se você Ctrl-Cestiver nele, todos os processos iniciados por esse script receberão um SIGINT.
Se você também o iniciou em segundo plano (ainda a partir de um shell interativo), ele também será iniciado em seu próprio grupo de processos.
Você pode descobrir mais sobre grupos de processos com:
ps -j
( j
é para job control
qual é o comportamento de shells que executam linhas de comando em grupos de processos para poder gerenciá-los (primeiro plano/plano de fundo/matar/suspender/resumir)).
Você pode descobrir sobreempregosdo seu shell interativo com o jobs
comando (embora não todos os processos nele). jobs -p
mostrará o ID do grupo de processos.
Você pode eliminar os membros de um grupo de processos enviando um sinal para -x
onde x
está o ID do grupo de processos (PGID) ou usando especificações de trabalhos com %job-number
. Por exemplo:
$ sleep 30 | sleep 40 &
[1] 6950 6951
$ ps -j
PID PGID SID TTY TIME CMD
6031 6031 6031 pts/3 00:00:00 zsh
6950 6950 6031 pts/3 00:00:00 sleep
6951 6950 6031 pts/3 00:00:00 sleep
6952 6952 6031 pts/3 00:00:00 ps
$ kill -- -6950
[1] + terminated sleep 30 | sleep 40
$ sleep 30 | sleep 40 &
[1] 6955 6957
$ jobs
[1] + running sleep 30 | sleep 40
$ kill %1
[1] + terminated sleep 30 | sleep 40
Agora, se não for iniciado a partir de um shell interativo, seu script terminará no mesmo grupo de processos que seu pai. Portanto, matar esse grupo de processos pode acabar matando vários outros processos que você não deseja eliminar.
O que você poderia fazer em seu script é iniciar você mesmo o grupo de processos.
Gosto adicionando:
[ "$(ps -o pgid= -p "$$")" -eq "$$" ] ||
exec perl -e 'setpgrp or die "setpgrp; $!"; exec @ARGV' -- "$0" "$@"
(que reexecuta o script após ter chamado perl
para iniciar um novo grupo de processos se detectarmos que não somos líderes do grupo de processos) no início do seu script.
Dessa forma, você garante que o script será executado em seu próprio grupo de processos.
O que isso significa é que se você fizer isso:
something | myscript | something
em um shell interativo, é provável que myscript
não seja o líder do grupo de processos. Ao fazer o que setpgrp
foi dito acima, o script não estará mais no grupo de processos em primeiro plano do terminal, o que significa que Ctrl-Cnão o encerrará.