Como 'grep' um fluxo contínuo?

729

É possível usar grepem um fluxo contínuo?

O que quero dizer é uma espécie de tail -f <file>comando, mas com grepa saída para manter apenas as linhas que me interessam.

Eu tentei, tail -f <file> | grep patternmas parece que grepsó pode ser executado quando tailterminar, ou seja, nunca.

Matthieu Napoli
fonte
9
É muito provável que o programa que gera o arquivo não esteja liberando sua saída.
23411 Steve-o
tail -f fileobras (I ver a nova saída em tempo real)
Matthieu Napoli
6
Seria apropriado para unix.stackexchange.com
Luc M
@Luc de fato, não tinha pensado nisso
Matthieu Napoli
Pode não haver novas linhas no seu fluxo de entrada? Nesse caso, o grep não continuará.
Lynch

Respostas:

1327

Ative o grepmodo de buffer de linha ao usar o BSD grep (FreeBSD, Mac OS X etc.)

tail -f file | grep --line-buffered my_pattern

Você não precisa fazer isso no GNU grep (usado em praticamente qualquer Linux), pois ele será liberado por padrão (YMMV para outros gostos de Unix, como SmartOS, AIX ou QNX).

pouco
fonte
3
@MichaelNiemand você poderia usar o arquivo -F de cauda | grep - my_pattern com buffer em linha
jcfrei
47
@MichaelGoldshteyn Acalme-se. As pessoas votaram positivamente porque acham esta página quando pesquisam "grep line buffered" no Google e isso resolve um problema para eles que pode não ser exatamente o que você colocou como pergunta.
raine 15/02
4
Eu vim aqui tentando grep a saída de strace. Sem o --line-buffered, não vai funcionar.
sjas
5
@ MichaelGoldshteyn (e os defensores de seu comentário): Eu sempre tive esse problema tail -f | grepe o --line-bufferedresolve para mim (no Ubuntu 14.04, GNU grep versão 2.16). Onde está a lógica "usar buffer de linha se stdout é um tty" implementada? Em git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedé definido apenas pelo analisador de argumentos.
Aasmund Eldhuset
8
@MichaelGoldshteyn Estou no macOS usando BSD grep e sem --line-bufferedeu não tenho saída. No entanto, após o teste, parece que o GNU grep faz o que você descreve. Assim como a maioria das coisas do Unix, isso depende da implementação da sua plataforma. Como a pergunta não especificou plataforma, suas informações parecem falsas - após revisar o código do BSD grep e compará-lo ao GNU grep, o comportamento é definitivamente controlado pela opção --line-buffered. É que apenas o GNU grep é liberado por padrão.
Richard Waite
119

Eu uso tail -f <file> | grep <pattern>o tempo todo.

Esperará até que o grep seja liberado, não até que termine (estou usando o Ubuntu).

Irit Katriel
fonte
4
O que pode durar um pouco, então tente não ficar impaciente.
glglgl
Quanto tempo pode demorar aproximadamente?
Matthieu Napoli
@ Matthieu: Depende principalmente do que você deseja e do tamanho dos buffers no seu sistema operacional. Se o grep corresponder apenas a uma cadeia curta a cada poucas horas, levará dias antes da primeira descarga.
tripleee
13
Tail não usa buffer de saída - grep faz.
XzKto 23/08
7
Não, o grep não faz buffer de saída quando a saída está indo para um dispositivo tty, como está claramente nesta resposta. Faz buffer de linha! Esta é a resposta correta e deve ser a resposta aceita. Veja meu comentário mais longo sobre a resposta atualmente aceita ( incorreta ) para obter mais detalhes.
Michael Goldshteyn
67

Eu acho que o seu problema é que o grep usa algum buffer de saída. Tentar

tail -f file | stdbuf -o0 grep my_pattern

ele definirá o modo de buffer de saída do grep como sem buffer.

XzKto
fonte
7
E isso tem a vantagem de poder ser usado para muitos outros comandos além grep.
Peter V. Mørch
4
No entanto, como eu descobri depois de jogar mais com ele, alguns comandos apenas liberam sua saída quando conectados a um tty, e por isso unbuffer(no expect-devpacote debian) é rei . Então, eu usaria unbuffer sobre stdbuf.
Peter V. Mørch 07/07/2012
5
@ Peter V. Mørch Sim, você está certo, o buffer nunca pode funcionar onde o stdbuf não pode. Mas acho que você está tentando encontrar um programa 'mágico' que sempre corrija seus problemas, em vez de entendê-lo. Criar um tty virtual não é uma tarefa relacionada. O Stdbuf faz exatamente o que queremos (define o buffer de saída padrão para dar valor), enquanto o unbuffer faz muitas coisas ocultas que talvez não desejemos (compare interativo topcom stdbuf e unbuffer). E realmente não existe uma solução 'mágica': o buffer do buffer também falha algumas vezes, por exemplo, o awk usa uma implementação de buffer diferente (o stdbuf também falha).
XzKto
2
"Mas acho que você está tentando encontrar um programa 'mágico' que sempre corrija seus problemas, em vez de entendê-los". - Eu acho que você está certo! ;-)
Peter V. Mørch
1
Alguns mais informações sobre stdbuf, `unbuffer e tamponamento stdio em pixelbeat.org/programming/stdio_buffering
Tor Klingberg
13

Se você deseja encontrar correspondências no arquivo inteiro (não apenas no final) e deseja que ele fique aguardando novas correspondências, isso funciona perfeitamente:

tail -c +0 -f <file> | grep --line-buffered <pattern>

O -c +0sinalizador diz que a saída deve iniciar 0bytes ( -c) desde o início ( +) do arquivo.

Ken Williams
fonte
12

Na maioria dos casos, você pode tail -f /var/log/some.log |grep fooe funcionará bem.

Se você precisar usar vários greps em um arquivo de log de execução e você achar que você tem nenhuma saída, pode ser necessário furar o --line-bufferedinterruptor em seu meio grep (s), assim:

tail -f /var/log/some.log | grep --line-buffered foo | grep bar
Dale Anderson
fonte
7

você pode considerar esta resposta como aprimoramento .. geralmente estou usando

tail -F <fileName> | grep --line-buffered  <pattern> -A 3 -B 5

-F é melhor no caso de rotação do arquivo (-f não funcionará corretamente se o arquivo for girado)

-A e -B é útil para obter linhas imediatamente antes e após a ocorrência do padrão. Esses blocos aparecerão entre os separadores de linhas tracejadas

Mas para mim eu prefiro fazer o seguinte

tail -F <file> | less

isso é muito útil se você deseja pesquisar dentro de logs transmitidos. Quero dizer, voltar e avançar e olhar profundamente

mebada
fonte
4
grep -C 3 <pattern>, substitui -A <N> e -B <N> se N for o mesmo.
AKS 2/17
6

Não vi ninguém oferecer o meu aval habitual para isso:

less +F <file>
ctrl + c
/<search term>
<enter>
shift + f

Eu prefiro isso, porque você pode usar ctrl + cpara parar e navegar pelo arquivo sempre que quiser e depois pressionar shift + fpara retornar à pesquisa de transmissão ao vivo.

Hans.Loven.work
fonte
4

sed seria uma escolha melhor ( editor de stream )

tail -n0 -f <file> | sed -n '/search string/p'

e, se você quiser que o comando tail saia, depois de encontrar uma sequência específica:

tail --pid=$(($BASHPID+1)) -n0 -f <file> | sed -n '/search string/{p; q}'

Obviamente, um basismo: $ BASHPID será o ID do processo do comando tail. O comando sed é o próximo após a cauda no pipe, portanto, o ID do processo sed será $ BASHPID + 1.

Christian Herr
fonte
1
A suposição de que o próximo processo iniciado no sistema ( $BASHPID+1) será seu é falsa em muitas situações, e isso não ajuda a resolver o problema de buffer, que é provavelmente o que o OP estava tentando perguntar. Em particular, recomendando sedao longo grepaqui parece ser apenas uma questão de preferência (duvidosa). (Você pode obter p;qcomportamento com grep -m 1se esse é o ponto que você está tentando entregar.)
tripleee
Funciona, o comando sed imprime cada linha assim que estiver pronto, o comando grep com --line-bufferednão. Eu sinceramente não entendo a menos 1.
MUY Bélgica
Até agora foi estabelecido que o buffer é o problema do grep . Nenhuma ação especial é necessária para lidar com o buffer de linha usando sed , é o comportamento padrão, portanto, minha ênfase no fluxo de palavras . E verdade, não há garantia de que $ BASHPID + 1 seja o pid correto a seguir, mas como a alocação de pid é seqüencial e o comando canalizado recebe um pid imediatamente a seguir, é totalmente provável.
Christian Herr
1

Sim, isso realmente funcionará bem. Grepe a maioria dos comandos Unix opera em fluxos uma linha por vez. Cada linha que sai da cauda será analisada e transmitida se corresponder.

Caleb
fonte
2
Isso não está realmente correto. Se grepfor o último comando na cadeia de tubulação, ele funcionará como você explica. No entanto, se estiver no meio, ele armazenará em buffer cerca de 8k de saída por vez.
Mahmoud Al-Qudsi
1

Este comando funciona para mim (Suse):

mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN  >> logins_to_mail

coletando logons no serviço de correio

user10584393
fonte
-1

você certamente não terá sucesso com

tail -f /var/log/foo.log |grep --line-buffered string2search

quando você usa "colortail" como um pseudônimo para cauda, ​​por exemplo. na festança

alias tail='colortail -n 30'

você pode verificar por tipo de alias se isso gera algo como tail isan alias of colortail -n 30 . então você tem o seu culpado :)

Solução:

remova o alias com

unalias tail

verifique se você está usando o binário real 'real' por este comando

type tail

que deve gerar algo como:

tail is /usr/bin/tail

e então você pode executar seu comando

tail -f foo.log |grep --line-buffered something

Boa sorte.

user882786
fonte
-4

Use awk (outro ótimo utilitário bash) em vez de grep, onde você não tem a opção de buffer de linha! Ele transmitirá continuamente seus dados da cauda.

é assim que você usa grep

tail -f <file> | grep pattern

É assim que você usaria o awk

tail -f <file> | awk '/pattern/{print $0}'
Atif
fonte
6
Isso não está correto; O Awk out of the box realiza buffer de linha, assim como a maioria das outras ferramentas Unix padrão. (Além disso, o {print $0}é redundante, como impressão, é a acção padrão quando uma condição passa.)
tripleee