Vários pipes em um log com cauda - o último pipe nunca recebe stdin

Vários pipes em um log com cauda - o último pipe nunca recebe stdin

Entrei em um estado interessante no Ubuntu. As etapas abaixo descrevem melhor.

Com um único cano, estou vendo o que espero

# In shell A
tail -f foo.log | grep aaa

# In shell B
echo aaa >> foo.log

# Shell A prints out `aaa`

Mas com vários pipes não estou vendo nada

# In shell A
tail -f foo.log | grep aaa | grep bbb

# In shell B
echo aaa bbb >> foo.log

# Nothing ever prints in shell A

Mas funciona bem se eu estiver apenas ecoando.

echo 'aaa bbb' | grep aaa | grep bbb

Esta é minha tentativa de criar uma reprodução mínima - originalmente encontrei o problema ao tentar obter logs do adb logcat (ferramentas de desenvolvimento Android). Eu tentei zsh, bash e fish também.

Presumi que tivesse algo a ver com meu limite do inotify watcher, mas ultrapassá-lo não mudou nada.

Responder1

Isso ocorre por causa do buffer no pipe, que em geral não se preocupa com linhas e pode acumular dados.

Acho que tail -fusa buffer de linha sozinho; e o último grepgrava no tty, portanto também usa buffer de linha. Portanto, seu primeiro exemplo funciona.

Mas grepno meio é diferente e você precisa ajustar seu comportamento forçando o buffer de linha ou desabilitando o buffer. Os comandos abaixo funcionarão conforme o esperado.

  • Se o seu grepsuporte --line-buffered(no Ubuntu):

      tail -f foo.log | grep --line-buffered aaa | grep bbb
    
  • Soluções mais genéricas (funcionarão com muitos filtros diferentes grep):

      tail -f foo.log | unbuffer -p grep aaa | grep bbb
      tail -f foo.log | stdbuf -oL grep aaa | grep bbb
      tail -f foo.log | stdbuf -o0 grep aaa | grep bbb
    

Veja man 1 grep, man 1 unbuffere man 1 stdbufpara detalhes e peculiaridades.

Notas:

  • Nenhuma das soluções é portátil ( grep --line-bufferede não é especificada pelo POSIX).unbufferstdbuf
  • Se você puder fazer isso, grep --line-buffereda escolha será sua. Não faz sentido usar ferramentas extras.
  • Pergunta relacionada sobre Unix e Linux SE:Desative o buffer no pipe.
  • unbufferestdbuf trabalhar de maneiras completamente diferentes.
  • Com stdbuf, buffer de linha ( -oL) deve ser preferido a nenhum buffer ( -o0) aqui, porque
    • provavelmente tem um desempenho melhor,
    • e outras partes do seu tubo usam buffer de linha de qualquer maneira.
  • Se a sua última grepgravação fosse em outro arquivo, ele se comportaria como o outro arquivo grep. Nesse caso, se você deseja que as linhas apareçam imediatamente no arquivo final, você também deve modificar o comportamento do último arquivo grep.
  • Em fish, if grepé uma função wrapper, você pode não obter o comportamento desejado com --line-buffered. Usar command grep --line-buffered. Veja esta pergunta:O tubo de saída aguarda EOF emfish.

Nota lateral: tail foo.log | grep aaa | grep bbb(ou seja, tailsem -f) não causa o problema porque tailexiste. Ao tailsair, o primeiro grepdetecta EOF, libera seu buffer e sai, então o segundo grepfaz o mesmo.

informação relacionada