Comment «grep» un flux continu?

729

Est-il possible de l'utiliser grepsur un flux continu?

Ce que je veux dire, c'est une sorte de tail -f <file>commande, mais avec grepsur la sortie pour ne garder que les lignes qui m'intéressent.

J'ai essayé tail -f <file> | grep patternmais il semble que grepcela ne peut être exécuté qu'une fois tailterminé, c'est-à-dire jamais.

Matthieu Napoli
la source
9
Il est fort probable que le programme générant le fichier ne vide pas sa sortie.
Steve-o
tail -f filefonctionne (je vois la nouvelle sortie en temps réel)
Matthieu Napoli
6
Conviendrait
Luc M
@Luc en effet, n'y a pas pensé
Matthieu Napoli
Peut-être qu'il n'y a pas de nouvelles lignes dans votre flux d'entrée? Si tel est le cas, grep ne procédera pas.
Lynch

Réponses:

1328

Activer le grepmode de mise en mémoire tampon de ligne lors de l'utilisation de BSD grep (FreeBSD, Mac OS X, etc.)

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

Vous n'avez pas besoin de le faire pour GNU grep (utilisé sur à peu près n'importe quel Linux) car il sera vidé par défaut (YMMV pour d'autres likes Unix tels que SmartOS, AIX ou QNX).

un peu
la source
3
@MichaelNiemand, vous pouvez utiliser le fichier tail -F | grep --line-buffered my_pattern
jcfrei
47
@MichaelGoldshteyn Allez-y doucement. Les gens la votent car ils trouvent cette page lorsqu'ils recherchent sur Google "grep line buffered" et cela résout un problème pour eux qui peut ne pas être exactement celui posé comme question.
raine
4
Je suis venu ici en essayant de récupérer la sortie de strace. Sans --line-bufferedcela, cela ne fonctionnera pas.
sjas
5
@MichaelGoldshteyn (et les votants de son commentaire): J'ai toujours eu ce problème avec tail -f | grep, et le --line-bufferedrésout pour moi (sur Ubuntu 14.04, GNU grep version 2.16). Où la logique "utiliser la mise en mémoire tampon de ligne si stdout est un tty" est-elle implémentée? Dans git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedest défini uniquement par l'analyseur d'arguments.
Aasmund Eldhuset
8
@MichaelGoldshteyn Je suis sur macOS en utilisant grep BSD et sans --line-bufferedobtenir aucune sortie. Cependant, après les tests, il semble que GNU grep fasse ce que vous décrivez. Donc, comme la plupart des choses Unix, cela dépend de la mise en œuvre de votre plate-forme. Étant donné que la question ne spécifiait pas la plate-forme, vos informations semblent fausses - après avoir examiné le code de grep BSD et l'avoir comparé à grep GNU, le comportement est définitivement contrôlé par l'option --line-buffered. C'est juste que seul GNU grep est vidé par défaut.
Richard Waite
119

J'utilise tail -f <file> | grep <pattern>tout le temps.

Il attendra jusqu'à ce que grep se vide, pas jusqu'à ce qu'il se termine (j'utilise Ubuntu).

Irit Katriel
la source
4
Ce qui peut durer un certain temps, alors essayez de ne pas vous impatienter.
glglgl
Combien de temps cela peut-il prendre environ?
Matthieu Napoli
@Matthieu: dépend principalement de ce que vous attendez et de la taille des tampons sur votre système d'exploitation. Si le grep ne correspond qu'à une chaîne courte toutes les quelques heures, il faudra plusieurs jours avant le premier rinçage.
tripleee
13
Tail n'utilise pas de tampon de sortie - grep le fait.
XzKto
7
Non, grep ne fait pas de tampon de sortie lorsque la sortie va vers un périphérique tty, comme c'est clairement le cas dans cette réponse. Il met en mémoire tampon la ligne! Ceci est la bonne réponse et devrait être la réponse acceptée. Voir mon commentaire plus long sur la réponse actuellement acceptée ( mauvaise ) pour plus de détails.
Michael Goldshteyn
67

Je pense que votre problème est que grep utilise un tampon de sortie. Essayer

tail -f file | stdbuf -o0 grep my_pattern

il définira le mode de tampon de sortie de grep sur non tamponné.

XzKto
la source
7
Et cela a l'avantage de pouvoir être utilisé pour de nombreuses autres commandes en plus grep.
Peter V. Mørch
4
Cependant, comme je l'ai découvert après avoir joué plus avec, certaines commandes ne vident leur sortie que lorsqu'elles sont connectées à un tty, et pour cela, unbuffer(dans le expect-devpaquet sur debian) est roi . J'utiliserais donc un tampon non plus sur stdbuf.
Peter V. Mørch
5
@Peter V. Mørch Oui, vous avez raison, le tampon peut parfois fonctionner là où stdbuf ne peut pas. Mais je pense que vous essayez de trouver un programme «magique» qui résoudra toujours vos problèmes au lieu de comprendre votre problème. La création d'un terminal virtuel n'est pas une tâche indépendante. Stdbuf fait exactement ce que nous voulons (définit le tampon de sortie standard pour donner de la valeur), tandis que unbuffer fait beaucoup de choses cachées que nous ne voulons peut-être pas (comparez interactivement topavec stdbuf et unbuffer). Et il n'y a vraiment pas de solution «magique»: l'imbuffeur échoue parfois aussi, par exemple awk utilise une implémentation de tampon différente (stdbuf échouera aussi).
XzKto
2
"Mais je pense que vous essayez de trouver un programme" magique "qui résoudra toujours vos problèmes au lieu de comprendre votre problème." - Je pense que tu as raison! ;-)
Peter V. Mørch
1
Quelques informations supplémentaires sur stdbuf, `unbuffer, et stdio buffering sur pixelbeat.org/programming/stdio_buffering
Tor Klingberg
13

Si vous voulez trouver des correspondances dans le fichier entier (pas seulement la queue), et que vous voulez qu'il reste assis et attende de nouvelles correspondances, cela fonctionne bien:

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

L' -c +0indicateur indique que la sortie doit commencer 0bytes ( -c) à partir du début ( +) du fichier.

Ken Williams
la source
12

Dans la plupart des cas, vous pouvez tail -f /var/log/some.log |grep fooet cela fonctionnera très bien.

Si vous avez besoin d'utiliser plusieurs greps sur un fichier journal en cours d' exécution et que vous trouvez que vous obtenez pas de sortie, vous devrez peut - être coller le --line-bufferedcommutateur dans votre milieu grep (s), comme suit:

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

vous pouvez considérer cette réponse comme une amélioration .. habituellement j'utilise

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

-F est meilleur en cas de rotation du fichier (-f ne fonctionnera pas correctement si le fichier tourne)

-A et -B sont utiles pour obtenir des lignes juste avant et après l'occurrence du motif .. ces blocs apparaîtront entre les séparateurs de lignes en pointillés

Mais pour moi, je préfère faire ce qui suit

tail -F <file> | less

cela est très utile si vous souhaitez rechercher dans les journaux en streaming. Je veux dire revenir en arrière et regarder en profondeur

mebada
la source
4
grep -C 3 <pattern>, remplace -A <N> et -B <N> si N est identique.
AKS
6

Personne n'a proposé mon go-to habituel pour cela:

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

Je préfère cela, car vous pouvez utiliser ctrl + cpour arrêter et parcourir le fichier à tout moment, puis appuyez simplement sur shift + fpour revenir à la recherche en direct en streaming.

Hans.Loven.work
la source
4

sed serait un meilleur choix ( éditeur de flux )

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

puis si vous vouliez quitter la commande tail une fois que vous avez trouvé une chaîne particulière:

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

Évidemment, un bashisme: $ BASHPID sera l'identifiant du processus de la commande tail. La commande sed est la suivante après la queue dans le tube, donc l'id du processus sed sera $ BASHPID + 1.

Christian Herr
la source
1
L'hypothèse selon laquelle le prochain processus démarré sur le système ( $BASHPID+1) sera le vôtre est fausse dans de nombreuses situations, et cela ne résout en rien le problème de la mise en mémoire tampon, ce qui est probablement ce à quoi l'OP tentait de se renseigner. En particulier, en recommandant sedplus grepsemble ici comme simplement une question de (douteuse) préférence. (Vous pouvez avoir un p;qcomportement grep -m 1si c'est le point que vous essayez de livrer.)
tripleee
Fonctionne, la commande sed imprime chaque ligne dès qu'elle est prête, la commande grep --line-bufferedne l'a pas fait. Je ne comprends vraiment pas le moins 1.
MUY Belgium
Il est jusqu'à présent établi que la mise en mémoire tampon est le problème avec grep . Aucune action spéciale n'est requise pour gérer la mise en mémoire tampon de ligne à l'aide de sed , c'est un comportement par défaut, d'où mon accent sur le flux de mots . Et vrai, il n'y a aucune garantie que $ BASHPID + 1 sera le bon pid à suivre, mais comme l'allocation de pid est séquentielle et que la commande canalisée se voit attribuer un pid immédiatement après, c'est tout à fait probable.
Christian Herr
1

Oui, cela fonctionnera très bien. Grepet la plupart des commandes Unix fonctionnent sur les flux une ligne à la fois. Chaque ligne qui sort de la queue sera analysée et transmise si elle correspond.

Caleb
la source
2
Ce n'est pas vraiment correct. Si grepc'est la dernière commande de la chaîne de tuyaux, elle agira comme vous l'expliquez. Cependant, s'il est au milieu, il tamponnera environ 8k de sortie à la fois.
Mahmoud Al-Qudsi
1

Cette commande fonctionne pour moi (Suse):

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

collecte des connexions au service de messagerie

user10584393
la source
-1

vous ne réussirez certainement pas avec

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

lorsque vous utilisez "colortail" comme alias pour la queue, par exemple. en bash

alias tail='colortail -n 30'

vous pouvez vérifier par type d'alias si cela produit quelque chose comme un isias de queue isan de colortail -n 30. alors vous avez votre coupable :)

Solution:

supprimer l'alias avec

unalias tail

assurez-vous que vous utilisez le «vrai» binaire de queue par cette commande

type tail

qui devrait produire quelque chose comme:

tail is /usr/bin/tail

puis vous pouvez exécuter votre commande

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

Bonne chance.

user882786
la source
-4

Utilisez awk (un autre excellent utilitaire bash) au lieu de grep où vous n'avez pas l'option de mise en mémoire tampon de ligne! Il diffusera en continu vos données depuis la queue.

voici comment vous utilisez grep

tail -f <file> | grep pattern

Voici comment utiliser awk

tail -f <file> | awk '/pattern/{print $0}'
Atif
la source
6
Ce n'est pas correct; Awk prêt à l'emploi effectue la mise en mémoire tampon des lignes, comme la plupart des autres outils Unix standard. (De plus, le {print $0}est redondant, car l'impression est l'action par défaut lorsqu'une condition passe.)
tripleee