Pipe Fail (141) quando piping output into tee - why?

3

Um exemplo deve esclarecer minha dúvida. Esse comportamento faz sentido para mim:

$ echo hi | cat
hi
$ echo hi | tee >(cat)
hi
hi

O primeiro caso é óbvio. No segundo caso, enviamos "hi" para o tee usando a substituição de comandos, e um "hi" é impresso pelo tee 'd cat , enquanto outro é impresso pelo pipe de passagem do tee . Até agora tudo bem ...

Mas o que acontece com o primeiro "oi" neste caso:

$ echo hi | tee >(echo yo)
yo

O código de retorno é 141, um pipe falha. O que poderia estar causando isso?

Estou executando o Mac OSX El Capitain, bash no aplicativo terminal padrão

    
por Jonah 04.04.2016 / 05:09

2 respostas

5

Acho que descobri como ajustar sua experiência para transformá-lo em algo que outras pessoas poderão reproduzir:

$ (echo hello; sleep 1; echo world) | tee >(cat)
hello
hello                                                       … and, after a brief delay,
world
world

$ echo "$?"
0

$ (echo hello; sleep 1; echo world) | tee >(echo yo)
yo
hello

$ echo "$?"
141

Como você espera entender, >(command) cria um pipe para um processo executando command . A entrada padrão de command está conectada a um nome de caminho que outros comandos na linha de comando (neste caso, tee ) pode abrir e escrever para. Quando command é cat , o processo fica lá e lê de stdin até obter um EOF. Nesse caso, tee não tem problema escrevendo todos os dados que lê do seu stdin para o pipe.

Mas, quando command é echo yo , o processo grava yo no stdout e sai imediatamente. Isso causa um problema para tee ; quando grava em um pipe sem nenhum processo na outra ponta, ele recebe um sinal SIGPIPE.

Aparentemente, a versão do OS X de tee grava primeiro no (s) arquivo (s) na linha de comando e, em seguida, no stdout. Então, no seu exemplo ( echo hi | tee >(echo yo) ), tee recebe a falha do pipe em sua primeira gravação. Considerando que, a versão de tee no Linux e Cygwin escreve para o stdout primeiro , então ele consegue escrever hi na tela antes de morrer. No meu exemplo aprimorado, tee morre ao gravar hello no pipe, por isso não tem a chance de ler e escrever world .

    
por 04.04.2016 / 07:25
1

Para visualizar o que está acontecendo, compare as duas variações a seguir:

bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'

bash -c 'wait_and_tee () { sleep 1; tee "$@"; };
         echo hi | wait_and_tee >(echo yo); echo $?'

Observe o que está acontecendo na primeira variação?

$ bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'     
hi
0
$ yo

O comando na substituição do processo sleep 1; echo yo é executado em paralelo com os comandos externos e o bash não espera que ele termine. Então a sequência de eventos é:

  • Os três comandos echo hi , sleep 1; echo yo e tee são iniciados em paralelo.
  • echo hi escreve hi em sua saída (o | pipe).
  • tee lê o canal e grava em sua saída padrão e em seu argumento de linha de comando, que é outro canal criado por >(…) . Isso resulta em uma cópia de hi impressa no terminal e uma cópia no buffer do >(…) pipe.
  • Em paralelo ao ponto de marcador anterior, echo hi exits, que fecha a extremidade de gravação do | pipe.
  • tee percebe que atingiu o fim de seu arquivo de entrada. Ele gravou todos os seus dados, então sai.
  • Da perspectiva de bash, ambos os lados do tubo saíram, então o comando acabou. Como tee retornou 0, o status do pipeline é 0.
  • Um segundo depois, sleep 1 termina e echo yo é executado.

Na segunda variação, forço o echo yo a terminar antes de tee , forçando-o a terminar antes que tee seja iniciado. Desta vez, a sequência de eventos é:

  • Os três comandos echo hi , echo yo e sleep 1 são iniciados em paralelo.
  • echo hi escreve hi em sua saída (o | pipe).
  • Em paralelo ao marcador anterior, echo yo imprime yo e sai.
  • Um segundo depois, sleep 1 exits e tee começam.
  • teehi de sua entrada e tenta gravá-la no terminal (sua saída padrão) e o pipe resultante de >(…) passou como um argumento. Como o processo que tinha esse pipe aberto para leitura ( echo yo ) foi encerrado há um segundo, a tentativa de gravar no pipe falha com SIGPIPE (sinal 13, que relatórios de shell como 128 + signal_number ).

Como o G-Man explica , se hi é exibido no segundo caso, depende se tee tenta para gravar sua saída padrão ou seu argumento de arquivo primeiro.

Sem as chamadas de sleep , o tempo poderia ir de qualquer forma (eu recebo cerca de metade / metade sob o Linux, um kernel diferente pode tornar um tempo muito mais provável que o outro).

    
por 05.04.2016 / 02:36

Tags