Inicie processos em segundo plano em um grupo e depois elimine todos eles

Inicie processos em segundo plano em um grupo e depois elimine todos eles

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 curltudo 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 controlqual é 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 jobscomando (embora não todos os processos nele). jobs -pmostrará o ID do grupo de processos.

Você pode eliminar os membros de um grupo de processos enviando um sinal para -xonde xestá 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 perlpara 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 myscriptnão seja o líder do grupo de processos. Ao fazer o que setpgrpfoi 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á.

informação relacionada