esperar falha no comando

esperar falha no comando

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 teedois 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_pipelineapenas 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 waitcomando 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:

  1. adicionou chaves ao redor do teepipeline, por sugestão de Jordanm. (No entanto, os resultados não mudaram.)
  2. 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, zshnão se podia esperar por empregos já extintos. Isso foi alterado na versão 5.0.8 em 2014. Veja a mudança.

Aqui, você pode simplesmente redirecionar stderr para /dev/nullignorar o problema:

wait $pid 2> /dev/null

Observe que em:

{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &

como uma otimização, zshnã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.

pastenã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 $pidfeche 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, waitpor precaução.

Observe que aqui você poderia usar a coprocpara evitar os fifos temporários:

coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait

(observe que waittambém espera pelo coprocs).

informação relacionada