_
~ DAMOLAB - Les tambouilles textes, teks et hacks ~
"La liberté consiste à choisir entre deux esclavages : l'égoïsme et la conscience. Celui qui choisit la conscience est l'homme libre." - Victor HUGO
DAMOLAB (#TAGS) _./._ XBNF blog _./._ Blog _./._

Cas particulier d'un comportement de deux grep à la suite d'un tail -f


| FR | par DAMO | | 03/01/2026 15h00 | (last update 14/01/2026 20h24) | Commentaires |
#DEV #LINUX
Quoi de pas mieux commencer 2026 avec une commande shell que j'utilise depuis longtemps...
Pourtant un détail important m'avait pourtant échappé jusque la, la bufferisation ...
|


1) Reproduction du problème



Quel problème et avec quelles commandes ?

Je souhaite envoyer des commandes entre deux processus en utilisant de fichiers d'échanges,
les pipe nommées peuvent être utilisée (i).

Je ne vais pas faire durer le suspense plus longtemps, voici la commande qui me pose problème:
 touch /tmp/cmd ; tail -f /tmp/cmd | grep test | grep test 


Si dans une autre console je fais:
 echo __test__ >> /tmp/cmd 


Je m'attends à voir la ligne "__test__" apparaitre dans la console ou j'ai lancé le tail -f ...

Avec un seul grep oui c'est bon:
 tail -f /tmp/cmd | grep test 


Avec un cat ou tee juste après le tail c'est OK:
 tail -f /tmp/cmd | cat | grep test 

 tail -f /tmp/cmd | tee | grep test 


Mais pas l'inverse avec un grep suivit d'une autre commande, rien ne transparait:
 tail -f /tmp/cmd | grep test |tail 

 tail -f /tmp/cmd | grep test |cat 


Pas mieux lors d'une redirection:
 tail -f /tmp/cmd | grep test > /tmp/test 


Le problème semble venir du grep mais pourquoi...


2) Explications sur la bufferisation



En posant la bonne question à google, je tombe sur Why can't I do two greps after a tail?.

A quoi sert la bufferisation ?
Tout simplement pour des raisons de performances, l'écriture fréquente dans un buffer est bien plus rapide en RAM que vers des fichiers.
La bufferisation est omni-présente un peu partout dans les librairies système, commandes Unix ou scripts ruby, python, ...
Sauf que selon les cas d'utilisation on s'attends un comportement précis, par exemple la prise en compte d'un retour à la ligne ou pas.

Dans mon cas, je cherche habituellement des chaines dans des logs, souvent volumineux, mes grep me retournent des résultats,
le comportement de bufferisation avec un tail -f n'est pas flagrant mais cela peut être très piègeux...

La commande grep est bufferisée et dans mon cas, si je refais ma commande qui pose problème:
 tail -f /tmp/cmd | grep test | grep test 


je ne verrais de résultat qu'à partir d'un certain volume de données, comment tester cela:

 yes __test___>> /tmp/cmd 


Equivalent en Bash:
while true; do
  echo __test___ >> /tmp/cmd
done


Equivalent en C-Shell:
while (1)
  echo __test___ >> /tmp/cmd
end


Equivalent en ruby:
#  ruby
f=open('/tmp/cmd','a')
while true
  f.puts '__test__'
end


3) Solution et fonctionnement du grep



L'option --line-buffered proposée sur la page:
 tail -f /tmp/cmd | grep --line-buffered test | grep --line-buffered test | grep test 


/!\\ Dans mon cas (analyse de logs), le mieux est d'ajouter cet alias dans mon profile de shell:
 alias grep /usr/bin/grep --line-buffered 


La oui, j'ai bien une sortie à chaque fois que je fais echo dans mon autre console,
à chaque ligne, c'est bien ce qui l'option.

Pourquoi cela fonctionne sans besoin de l'option dans le dernier grep ?

Ce que qui est dit sur la page, le grep ne bufferise pas s'il est rattaché à un tty, comment voir cela ?

1) lister les processus grep:
 ps -edf |grep 'grep test' 

damo      114222    4176  0 15:02 pts/1    00:00:00 grep test --line-buffered
damo      114223    4176  0 15:02 pts/1    00:00:00 grep test --line-buffered
damo      114224    4176  0 15:02 pts/1    00:00:00 grep test
damo      114228    3931  0 15:02 pts/0    00:00:00 grep grep test


/!\\ Ignorer le dernier "grep grep test" c'est juste la commande que je viens juste de lancer.

2) ensuite, pour voir sur quoi le stdout de mes processus grep sont rattachés:

 ls -l /proc/{PID}/fd/{file-descriptor ID} 


# ls -l /proc/11422[2-4]/fd/1
l-wx------ 1 damo damo 64 janv.  3 15:02 /proc/114222/fd/1 -> 'pipe:[313334]'
l-wx------ 1 damo damo 64 janv.  3 15:02 /proc/114223/fd/1 -> 'pipe:[313335]'
lrwx------ 1 damo damo 64 janv.  3 15:02 /proc/114224/fd/1 -> /dev/pts/1


Notez que:
  • l'utilisation de /proc est très pratique pour avoir toutes les infos système du noyau Linux
  • .../fd/1 correspond au descripteur de la "sortie standard" ou stdout ; 0 est pour stdin et 2 stderr
  • les crochets que j'utilise, c'est pour la forme, mes numéros de pid se suivent
  • le dernier grep en bout de chaine redirige vers pts, c'est votre console pts ou tty, non bufferisé
  • dans le cas d'une redirection (dernier grep), vous le verrez dans /proc/<pid>/fd/1


Je vois bien que mon processus grep écrit stdout à travers un pipe qui est maintenant bufferisé par ligne,
le dernier grep est rattaché à la console courante est n'est jamais bufferisé.


4) Autre Solution



Toutes les commandes ne propose pas de désactiver la bufferisation, cette alternative élégante le permet:
stdbuf -i0 -o0 -e0 <commande> 


Dans ce cas précis, rien n'est bufferisé, dans mon cas, pour bufferiser par ligne:
stdbuf -i0 -oL -eL <commande> 


Je l'ai testé sur un cut dans la même situation décrite initialement (tail -f).

5) Allez plus loin



L'utilisation de pipe nommé peut satisfaire des besoins.

Le fonctionnement est différent, l'écriture de donnée en RAM reste bloquée tant que la donnée n'est pas lu.


6) Pour conclure



En ruby, pour des prompts avec les entrée ou sortie standards, j'utilise ceci avec open, popen, ... :
 file_descriptor.sync=true 


Cela va synchroniser automatiquement tous les caractères, cela fonctionne mais pas top pour les performances,
cela dépends des cas d'utilisation.

Avec les sockets Unix, c'est un peu plus compliqué, mais le principe est le même, il faut gérer la bufferisation...


0.1) DamoLab


DamoLab@Sourceforge
DamoLab@DockerHub

0.2) Calculette composants radio


Electro-slide

0.3) Technologies


-- IA --
Mistral IA (le chat) (i)

-- Cyber-securité --
Shodan (i)11
Have I been pwned ? (i)
personal-data-leak-check
Osint
Zataz actualités

-- Adresses IP --
Mes Infos (IP, UA, ...)
info IP: WHOIS,ping,...
info IP: WHOIS,ping,...
info IP: Locallisation
info IP: abus
DNS checker

-- Mails --
GMX
Hushmail - mail temporaire sécurisé

encode/decode email for defeating bots
 $_='mailto: matt@org';s/(.)/chr(ord($1)+5)/eg;$_ 


-- Linux --
News LinuxFr
Mind-map Linux
Tutorials
crontab

-- Blogs Teks --
@TechWorldwithNana
@Cookieconnect
@GoCloudArchitects
@grafikart
@Underscore_
@cybernewsenfrancais


-- Geek Code --

décoder un GC

GCS d>+(---) s- a+ C++$ UL+>$ UOS++$
P++++>$ L++ E W++(++) N(+) o-- K+ w>--- O- M>+ PS+>+++ PE-- Y+@ t-(+) >5 X+ R>+ !tv b+
D---- D+ G e+++ h---- r+++ z+++



Rescue NEO...
(JS by Rezmason)


Nom/surnom:
Message:


revenir sur l'article
#DEV #LINUX

Aucun commentaire.

dans
une
galaxy
lointaine
Charte du site damolab.zapto.org est motorisé par

"La force est une sorte de fluide crée par tout être vivant, une énergie qui nous entoure et nous pénètre, et qui maintient la galaxie en un tout unique."
- Obi-Wan à Luke Yoda