O Stderr de scripts de shell canalizados nem sempre é exibido

2

Eu canalizei meus próprios scripts de shell para alguns testes e acidentalmente notei algo estranho. Ou seja, o stderr desses processos canalizados nem sempre é exibido na tela.

Eu simplifiquei os scripts e aqui está minha sessão com o bash:

$ cat file1
echo stdout
echo stderr 1>&2
$ cat file2
echo stdout2
echo stderr2 1>&2
$ cat file3
echo stdout3
echo stderr3 1>&2

$ ./file1 | ./file2 | ./file3
stderr2
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr2
stdout3
stderr3

Eu sei que o stdout dos dois primeiros scripts está perdido, já que o arquivo3 não lê nada, mas não importa aqui.

O que está acontecendo com as saídas stderr2 e stderr no segundo e terceiro casos, respectivamente?

    
por Quentin 18.02.2013 / 04:32

1 resposta

3

Ooh, uma condição de corrida! Há algumas coisas acontecendo aqui nos bastidores para produzir esse efeito.

Primeiro, se um processo tentar gravar em um pipe cuja outra extremidade tenha sido fechada, esse processo receberá um sinal SIGPIPE . Por padrão, isso encerra o processo. Por que você quer isso? Se você executar cat my_huge_file | head -3 , verá apenas as três primeiras linhas do arquivo e, por causa do mecanismo de sinalização, cat sabe parar após essas três linhas. Sem ele, ele leria o arquivo inteiro.

Em segundo lugar, todos os processos em um pipeline executados em paralelo e qual processo será concluído primeiro é imprevisível. Quando um processo termina, todos os seus descritores de arquivo são fechados - incluindo as extremidades de leitura de qualquer canal.

Então, o que está acontecendo é que às vezes, por exemplo, file3 termina completamente antes que file2 tenha escrito em sua stdout. Então, quando file2 tentar escrever, ele recebe SIGPIPE e sai sem atingir a linha echo stderr2 . Porém, outras vezes, file2 consegue gravar no stdout antes de file3 ser concluído e, nesse caso, ele continua.

    
por 18.02.2013 / 08:18