Dado este exemplo mínimo
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
ele gera LINE 1
e, em seguida, após um segundo, gera LINE 2
, conforme o esperado .
Se canalizarmos isso paragrep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
o comportamento é o mesmo do caso anterior, conforme esperado .
Se, alternativamente, canalizarmos isso paracat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
o comportamento é novamente o mesmo, como esperado .
No entanto , se canalizarmos para grep LINE
, e depois para cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
não há saída até que um segundo passe, e ambas as linhas aparecem na saída imediatamente, o que eu não esperava .
Por que isso está acontecendo e como posso fazer a última versão se comportar da mesma maneira que os três primeiros comandos?
Quando a saída do (pelo menos GNU)
grep
não é um terminal, ele armazena sua saída em buffer, que é o que causa o comportamento que você está vendo.grep
Você pode desabilitar isso usando a opção do GNU--line-buffered
:ou o
stdbuf
utilitário:Desativar buffer no pipe tem mais sobre este tópico.
Explicação simplificada
Como muitos utilitários, isso não sendo algo peculiar a um programa,
grep
varia sua saída padrão entre buffer de linha e buffer completo . No primeiro caso, a biblioteca C armazena os dados de saída na memória até que o buffer que contém esses dados seja preenchido ou um caractere de alimentação de linha seja adicionado a ele (ou o programa termine corretamente), após o que ele chamawrite()
para realmente gravar o conteúdo do buffer. No último caso, apenas o buffer na memória ficando cheio (ou o programa terminando corretamente) aciona o arquivowrite()
.Explicação mais detalhada
Esta é a explicação bem conhecida, mas um pouco errada. Na verdade, a saída padrão não é armazenada em buffer de linha, mas armazenada em buffer inteligente na biblioteca GNU C e na biblioteca BSD C. A saída padrão também é liberada quando a leitura da entrada padrão esgota seu buffer na memória (de entrada de pré-leitura) e a biblioteca C precisa chamar
read()
para buscar mais alguma entrada e está lendo o início de uma nova linha. (Uma razão para isso é evitar deadlock quando outro programa se conecta a ambas as extremidades de um filtro e espera poder operar linha por linha, alternando entre escrever no filtro e ler dele; como "coprocessos" no GNUawk
por exemplo.)Influência da biblioteca C
grep
e os outros utilitários fazem isso — ou, mais estritamente, as bibliotecas C que eles usam fazem isso, porque esse é um recurso definido de programação na linguagem C — com base no que eles detectam ser sua saída padrão. Se (e somente se) não for um dispositivo interativo, eles escolhem o buffer completo, caso contrário, eles escolhem o buffer inteligente. Um pipe é considerado um dispositivo não interativo, porque a definição de ser um dispositivo interativo, pelo menos no mundo do Unix e do Linux, é essencialmente aisatty()
chamada retornando true para o descritor de arquivo relevante.Soluções alternativas para desabilitar o buffer completo
Alguns utilitários
grep
têm opções idiossincráticas, como essas--line-buffered
que alteram essa decisão, que, como você pode ver, é nomeada incorretamente. Mas uma fração muito pequena dos programas de filtro que alguém poderia usar realmente tem essa opção.De maneira mais geral, pode-se usar ferramentas que se aprofundam nas partes internas específicas da biblioteca C e alteram sua tomada de decisão (que apresentam problemas de segurança se o programa a ser alterado for set-UID e também são específicas para bibliotecas C específicas e, de fato, são específicos para programas escritos ou em camadas em cima da linguagem C), ou ferramentas como essas
ptybandage
que não alteram as partes internas do programa, mas simplesmente interpõem um pseudo-terminal como saída padrão para que a decisão seja "interativa", para afetar isso.Leitura adicional
Usar
para fazer o grep não armazenar mais de uma linha por vez.