Você sabe, é claro, que $(…)
faz com que o (s) comando (s) dentro dos parênteses
para executar em um subshell. E você sabe, é claro, que jobs
é um shell embutido.
Bem, parece que jobs
apaga um trabalho da memória do shell
uma vez que sua morte tenha sido relatada .
Mas, quando você executa $(jobs)
, o comando jobs
é executado em um subshell,
por isso não tem a chance de dizer ao shell pai
(aquele que está executando o script) que a morte do trabalho em segundo plano
( ping
, no seu exemplo) foi reportado.
Então, toda vez que o shell gerar uma sub-shell para executar o $(jobs)
thingie,
esse subshell ainda tem uma lista completa de jobs
(ou seja, o trabalho ping
está lá, apesar de estar morto após a 5ª iteração),
e assim jobs
ainda (novamente) acredita que
ele precisa informar sobre o status do trabalho ping
(mesmo que esteja morto nos últimos quatro segundos).
Isso explica por que executar um comando jobs
não adulterado
dentro do loop faz com que ele saia como esperado:
depois de executar jobs
no shell pai , o shell pai sabe que
a rescisão do trabalho foi relatada ao usuário.
Por que é diferente no shell interativo?
Porque, sempre que um filho em primeiro plano de um shell interativo termina,
o shell informa sobre quaisquer trabalhos em segundo plano que tenham terminado 1
enquanto o processo de primeiro plano estava em execução.
Então, o ping
termina enquanto o sleep 1
está em execução,
e quando o sleep
terminar,
o shell relata a morte do trabalho em segundo plano.
Et voilà.
1 Pode ser mais preciso dizer "qualquer trabalho em segundo plano
que mudaram de estado enquanto o processo de primeiro plano estava em execução. ”
Acredito que também possa relatar sobre trabalhos que foram suspensos
( kill -SUSP
, o programático equivalente a Ctrl + Z )
ou tornar-se não suspenso ( kill -CONT
, que é o que o comando fg
faz).