
O roteiro abaixo não tem outro propósito senão ilustrar essa questão.
#!/usr/bin/env zsh
arbitrary_pipeline () {
shuf | tr a-z A-Z
}
tmpdir=$( mktemp -d )
mkfifo $tmpdir/{orig,alt}
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
pid=$!
paste $tmpdir/orig $tmpdir/alt
rm -rf $tmpdir
wait $pid
O script usa tee
dois pipes nomeados para dividir alguma entrada padrão (arbitrária) em dois fluxos, redireciona um deles para um pipeline arbitrário e passa os dois fluxos resultantes para as entradas paste
. Esquematicamente:
STDIN --- > --.- arbitrary_pipeline -.
\ \
paste `----<FIRST-ARGUMENT> `- <SECOND-ARGUMENT> --> STDOUT
(Neste caso arbitrary_pipeline
apenas embaralha seu stdin e converte para maiúsculas, mas, como o nome indica, pode ser qualquer coisa.)
A saída stdout do script parece boa, mas o wait
comando sempre falha:
% grep -iP 'z.*s.*h' /usr/share/dict/words | /tmp/test.sh
Nietzsche CITIZENSHIP'S
Zubeneschamali NIETZSCHE
Zubeneschamali's ZUBENESCHAMALI
citizenship CITIZENSHIP
citizenship's ZUBENESCHAMALI'S
/tmp/test.sh:wait:18: pid 26357 is not a child of this shell
O que estou fazendo de errado?
FWIW:
/usr/bin/env zsh --version
# zsh 5.0.7 (x86_64-pc-linux-gnu)
EDITAR% S:
- adicionou chaves ao redor do
tee
pipeline, por sugestão de Jordanm. (No entanto, os resultados não mudaram.) - substituído
&!
por&
em resposta ao comentário de Stéphane Chazelas. (Novamente, os resultados não mudaram.)
Responder1
Antes da versão 5.0.8, zsh
não se podia esperar por empregos já extintos. Isso foi alterado na versão 5.0.8 em 2014. Veja a mudançalá.
Aqui, você pode simplesmente redirecionar stderr para /dev/null
ignorar o problema:
wait $pid 2> /dev/null
Observe que em:
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
como uma otimização, zsh
não irá bifurcar um processo extra para arbitrary_pipeline
, ele irá executá-lo no mesmo processo que aquele que executa aquele subshell iniciado em segundo plano.
paste
não terminará antes de ver EOF em seu stdin, sendo seu stdin o canal onde $pid
(e seus filhos, se houver) está escrevendo na outra extremidade. Portanto, ele não verá eof até que $pid
(e filhos) feche todos os seus descritores de arquivo (normalmente apenas stdout) até o final do canal de escrita. A menos que $pid
feche explicitamente seu stdout (o que é muito incomum), isso só acontecerá quando ele sair.
O que isso significa é que paste
, na maioria dos casos, não sairá antes $pid
, ainda é uma boa ideia fazer isso, wait
por precaução.
Observe que aqui você poderia usar a coproc
para evitar os fifos temporários:
coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait
(observe que wait
também espera pelo coproc
s).