Étant donné cet exemple minimal
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
il sort LINE 1
et puis, après une seconde, sort LINE 2
, comme prévu .
Si nous le canalisons grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
le comportement est le même que dans le cas précédent, comme prévu .
Si, alternativement, nous cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
le comportement est à nouveau le même, comme prévu .
Cependant , si nous redirigeons vers grep LINE
, puis vers cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
il n'y a pas de sortie avant qu'une seconde ne passe et les deux lignes apparaissent immédiatement sur la sortie, ce à quoi je ne m'attendais pas .
Pourquoi cela se produit-il et comment puis-je faire en sorte que la dernière version se comporte de la même manière que les trois premières commandes?
cat
concatène les fichiers. Qu'essayez-vous de faire en jouantcat
?cat
lit simplementstdin
et sort dansstdout
. Bien sûr, j'ai posé cette question avec beaucoup de choses complexes à la place deecho
etcat
, mais celles-ci se sont révélées non pertinentes, car le problème apparaît avec des exemples beaucoup plus simples.Réponses:
Lorsque la
grep
sortie (au moins de GNU) n'est pas un terminal, elle met en mémoire tampon sa sortie, ce qui est à l'origine du comportement que vous voyez. Vous pouvez désactiver cela soit en utilisantgrep
l'--line-buffered
option GNU :ou l'
stdbuf
utilitaire:Désactiver la mise en mémoire tampon dans le tuyau a plus sur ce sujet.
la source
Explication simplifiée
Comme de nombreux utilitaires, ce n'est pas quelque chose de particulier à un programme,
grep
sa sortie standard varie entre être mise en mémoire tampon de ligne et entièrement mise en mémoire tampon . Dans le premier cas, les tampons de la bibliothèque C produisent des données en mémoire jusqu'à ce que le tampon contenant ces données soit rempli ou qu'un caractère de saut de ligne y soit ajouté (ou que le programme se termine proprement), après quoi il appellewrite()
pour réellement écrire le contenu du tampon. Dans ce dernier cas, seul le tampon en mémoire saturé (ou le programme se terminant proprement) déclenche lewrite()
.Explication plus détaillée
C'est l'explication bien connue, mais légèrement erronée. En fait, la sortie standard n'est pas mise en mémoire tampon de ligne mais mise en mémoire tampon intelligente dans la bibliothèque GNU C et la bibliothèque BSD C. La sortie standard est également vidée lorsque la lecture de l' entrée standard épuise son tampon en mémoire (d'entrée pré-lue) et la bibliothèque C doit appeler
read()
pour récupérer plus d'entrée et elle lit le début d'une nouvelle ligne. (Une des raisons pour cela est d'empêcher un blocage lorsqu'un autre programme se connecte aux deux extrémités d'un filtre et s'attend à pouvoir fonctionner ligne par ligne, en alternant entre l'écriture sur le filtre et la lecture de celui-ci; comme les "coprocesses" dans GNUawk
par exemple.)Influence de la bibliothèque C
grep
et les autres utilitaires le font - ou, plus strictement, les bibliothèques C qu'ils utilisent le font, car c'est une caractéristique définie de la programmation en langage C - en fonction de ce qu'ils détectent être leur sortie standard. Si (et seulement si) ce n'est pas un appareil interactif, ils choisissent la mise en mémoire tampon complète, sinon ils choisissent la mise en mémoire tampon intelligente. Un canal n'est pas considéré comme un périphérique interactif, car la définition d'un périphérique interactif, au moins dans le monde d'Unix et de Linux, est essentiellement l'isatty()
appel renvoyant true pour le descripteur de fichier correspondant.Solutions de contournement pour désactiver la mise en mémoire tampon complète
Certains utilitaires comme
grep
ont des options idiosyncratiques telles que celles--line-buffered
qui modifient cette décision, qui, comme vous pouvez le voir, est mal nommée. Mais une fraction infiniment petite des programmes de filtrage que l'on pourrait utiliser a en fait une telle option.Plus généralement, on peut utiliser des outils qui creusent dans les internes spécifiques de la bibliothèque C et changent sa prise de décision (qui ont des problèmes de sécurité si le programme à modifier est set-UID, et sont également spécifiques à des bibliothèques C particulières, et en fait sont spécifiques aux programmes écrits en ou superposés au langage C), ou à des outils tels que
ptybandage
ceux -ci qui ne modifient pas les internes du programme mais simplement interposent un pseudo-terminal en sortie standard afin que la décision apparaisse comme "interactive", pour affecter cela.Lectures complémentaires
la source
grep
, mais des appels de bibliothèque sous-jacents,setbuf
/setvbuf
. Je ne connais pas de référence fiable en ligne pour la norme C, mais par exemple les pages de manuel Linux et FreeBSD ainsi que la description POSIX de l'setvbuf
appeler "ligne tamponnée". Même la constante symbolique en est_IOLBF
.Utilisation
pour que grep ne mette pas en mémoire tampon plus d'une ligne à la fois.
la source