Fazer loop até que o processo em segundo plano seja concluído (com `jobs`) não funciona no script

Fazer loop até que o processo em segundo plano seja concluído (com `jobs`) não funciona no script

Estou tentando fazer um loop até que um processo em segundo plano (iniciado anteriormente no script) seja concluído. Um caso de teste facilmente reproduzível é:

ping -c 10 localhost &>/dev/null &

Na linha de comando, posso fazer um loop while [[ -n $(jobs) ]](enquanto $(jobs)não é nulo).

$ ping -c 10 localhost &>/dev/null &
[1] 19078
$ while [[ -n $(jobs) ]]; do echo -n .; sleep 1; done
.........[1]+  Done                    ping -c 5 localhost &> /dev/null

No entanto, as mesmas duas linhas em um script continuarão imprimindo .s até eu acertar Ctrl-C.

Estranhamente, se eu chamar jobsdentro do loop, o script termina conforme o esperado.

$ ./background-ping.sh
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Done                    ping -c 5 localhost &> /dev/null

Estou ciente de que existem outras maneiras de verificar se o trabalho em segundo plano foi concluído (por exemplo, verificação /proc), mas quero saber por que a verificação jobsnão funciona conforme o esperado.

Responder1

Se entendi seu problema, suas 2 linhas não pararão em um script.

Foi isso que fiz no meu:

ping -c 5 google.com &>/dev/null &
while [[ -n $(jobs -r) ]]; do echo -n "."; sleep 1; done

jobs -rverifica os processos em execução e a chamada do meu script funciona conforme o esperado, o script para quando o ping é concluído.

EDIT: Acho que em um script o processo pai é conhecido como um processo em execução e é por isso que jobscontinuo pensando que existe um processo em execução. Isso é uma hipótese

Essa é a resposta? (ou talvez eu realmente não entenda o que você quer dizer, meu inglês ruim pode ser o problema...)

Responder2

Sempre que você tiver problemas como esse, você deve sempre tentar imprimir suas variáveis ​​para entender o que está acontecendo. Neste caso, jobstambém retorna trabalhos finalizados. Se você executar isso:

ping -c 5 localhost &>/dev/null &
while [[ -n $(jobs | tee -a temp) ]]; do
    echo -n .; 
    sleep 1; 
done

Você verá a seguinte saída em temp:

[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[...]

Então, a saída jobsaqui nunca está vazia. Mesmo quando o trabalho termina, jobsainda retorna a Donemensagem. É por isso que a solução do Metal3d jobs -rfuncionou.

O que é mais confuso é por que a execução jobsdentro do loop faz com que ele funcione corretamente. A resposta terá algo a ver com o fato de while [[ -n $(jobs) ]]ser executado jobsem um subshell separado, mas não tenho certeza dos detalhes. Eu tenhopostou uma perguntasobre isso na U&L se alguém estiver interessado.

Responder3

Você pode usar waito comando que espera que os trabalhos em segundo plano sejam interrompidos

Exemplo:

$ ping -c 10 localhost &>/dev/null &
[29787]
$ wait
[1]+  Done ping -c 10 localhost &>/dev/null

O comando wait é bloqueado e quando pingterminar, o prompt é liberado e imprime uma mensagem.

Você pode usar is com vários comandos:

$ ping -c 5 localhost &>/dev/null & ping -c 5 facebook.com  &>/dev/null &
[1] 29867
[2] 29868
$ wait
[1]-  Done                    ping -c 5 localhost &>/dev/null
[2]+  Done                    ping -c 5 facebook.com &>/dev/null

informação relacionada