Testei o seguinte com bash e dash, e parece que trava de forma confiável:
[ -p pipe ] || mkfifo pipe
i=0
while [ $i -lt 10 ]; do
<pipe cat &
: $(( i+=1 ))
done
# sleep 1
echo hello world >pipe
wait
Se diminuirmos o limite de 10 para 1, ele não trava (pelo menos não de forma confiável). Dormir impede que ele trave.
Acho que o eco está acontecendo antes de todos os trabalhos em segundo plano terem aberto o pipe, então o EOF é gravado no pipe, fechando o pipe e encerrando alguns trabalhos em segundo plano. Os que chegam atrasados ficam presos esperando por um EOF que nunca chega.
Esse é o comportamento esperado? Parece um bug para mim, mas não tenho certeza se é meu bug ou do shell.
Além disso: sei que posso simplesmente escrever echo x | { <launch commands here> }
, e isso seria mais organizado, mas estou trabalhando em um comportamento do traço que já relatei como um bug .
EDIT: A versão abaixo termina consistentemente no bash, então parece provável que a intenção seja iniciar um grupo de processos em segundo plano e alimentá-los a partir de um único pipeline sem fazer nada especial.
echo hello world | {
i=0
while [ $i -lt 10 ]; do
cat &
: $(( i+=1 ))
done
}
wait
EDIT: abrir o pipe no pai (modo r/w para evitar bloqueios ) e duplicar o descritor de arquivo resultante para cada processo em segundo plano não parece resolver o problema e resulta em travamentos mesmo após o modo de suspensão.
[ -p pipe ] || mkfifo pipe
exec 3<>pipe
i=0
while [ $i -lt 10 ]; do
<&3 cat &
: $(( i+=1 ))
done
# sleep 1
echo hello world >pipe
wait
Você teria que rastrear isso para ver com certeza em que ordem tudo acaba acontecendo (por exemplo, com strace no Linux), mas observe que não há nenhuma ordem entre como os processos em segundo plano são executados e o restante do script de shell é executado. É bem possível que alguns dos processos em segundo plano posteriores sejam executados após o
echo
script principal ter terminado. Isso dependerá muito do SO e do ambiente ao redor.Então o que você pode obter é algo como isto:
Aqui, os primeiros N cats, ou melhor, os processos do shell prestes a lançar o cats bloqueiam ao tentar abrir o pipe, pois ainda não há um writer. Então, o shell prestes a ser executado
echo
abre o lado de gravação do pipe, deixando os cats terminarem suas aberturas. O echo escreve, um cat lê e, quando o echo termina, todos os cats veem que o pipe está fechado, pois não tem um writer; eles leem um EOF e saem. Então, depois disso, os últimos M processos em segundo plano tentam abrir o pipe e bloqueiam a abertura, pois não há um writer.Aqui,
o pipe é criado uma vez, e o mesmo fd já aberto é passado para todos os gatos. Ele não é aberto para cada gato individualmente, então nenhum deles bloqueia na abertura. Se alguns dos gatos conseguirem ler o fd antes que o eco tenha escrito alguma coisa, eles bloquearão na leitura esperando pelos dados. Quando o lado esquerdo fecha o pipe, todos os gatos leem um EOF, mesmo aqueles que primeiro leram sua cópia do fd depois que o eco terminou.
Esta pergunta foi respondida em conjunto por Greg Wooledge e Chet Ramey na lista de discussão "bash-help" do GNU.
Em resumo, há dois motivos pelos quais isso não funciona:
Com um único pipe, tudo funciona bem porque os processos param até que ambas as extremidades da conexão estejam abertas.
Múltiplas leituras/gravações simultâneas funcionam no Linux, mas esse não é um comportamento portátil.