Sempre que você tiver problemas como esse, sempre tente imprimir suas variáveis para entender o que está acontecendo. Nesse caso, jobs também retorna trabalhos concluídos. Se você executar isto:
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
[...]
Portanto, a saída de jobs aqui nunca está vazia. Mesmo quando o trabalho for concluído, jobs ainda retornará a mensagem Done . É por isso que a solução da Metal3d usando jobs -r funcionou.
O que é mais confuso é porque executar jobs dentro do loop faz com que ele funcione corretamente. A resposta será algo a ver com o fato de que while [[ -n $(jobs) ]] executa jobs em um subshell separado, mas não tenho certeza sobre os detalhes. Eu tenho postado uma pergunta sobre isso em U & L, se alguém estiver interessado.