
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 -f
usa buffer de linha sozinho; e o último grep
grava no tty, portanto também usa buffer de linha. Portanto, seu primeiro exemplo funciona.
Mas grep
no 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
grep
suporte--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 unbuffer
e man 1 stdbuf
para detalhes e peculiaridades.
Notas:
- Nenhuma das soluções é portátil (
grep --line-buffered
e não é especificada pelo POSIX).unbuffer
stdbuf
- Se você puder fazer isso,
grep --line-buffered
a escolha será sua. Não faz sentido usar ferramentas extras. - Pergunta relacionada sobre Unix e Linux SE:Desative o buffer no pipe.
unbuffer
estdbuf
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
grep
gravação fosse em outro arquivo, ele se comportaria como o outro arquivogrep
. Nesse caso, se você deseja que as linhas apareçam imediatamente no arquivo final, você também deve modificar o comportamento do último arquivogrep
. - Em
fish
, ifgrep
é uma função wrapper, você pode não obter o comportamento desejado com--line-buffered
. Usarcommand 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, tail
sem -f
) não causa o problema porque tail
existe. Ao tail
sair, o primeiro grep
detecta EOF, libera seu buffer e sai, então o segundo grep
faz o mesmo.