Forcer la mise en tampon de ligne de stdout lors du raccordement au té

117

Habituellement, stdoutest tamponné en ligne. En d'autres termes, tant que votre printfargument se termine par une nouvelle ligne, vous pouvez vous attendre à ce que la ligne soit imprimée instantanément. Cela ne semble pas tenir lorsque vous utilisez un tube vers lequel rediriger tee.

J'ai un programme C ++,, aqui génère des chaînes, toujours \nterminées, vers stdout.

Lorsqu'il est exécuté seul ( ./a), tout s'imprime correctement et au bon moment, comme prévu. Cependant, si je le dirige vers tee( ./a | tee output.txt), il n'imprime rien tant qu'il ne se ferme pas, ce qui va à l'encontre de l'objectif de l'utilisation tee.

Je sais que je pourrais le réparer en ajoutant un fflush(stdout)après chaque opération d'impression dans le programme C ++. Mais existe-t-il un moyen plus simple et plus propre? Existe-t-il une commande que je peux exécuter, par exemple, qui forcerait la mise stdouten tampon de ligne, même lorsque vous utilisez un tube?

houbysoft
la source

Réponses:

67

Essayez unbufferce qui fait partie du expectpackage. Vous l'avez peut-être déjà sur votre système.

Dans votre cas, vous l'utiliseriez comme ceci:

./a | unbuffer -p tee output.txt

( -pest pour le mode pipeline où un buffer lit depuis stdin et le passe à la commande dans le reste des arguments)

Suspendu jusqu'à nouvel ordre.
la source
Merci, cela a fonctionné, même si j'ai dû expectme compiler car unbufferne semble pas être inclus par défaut dans OS X.
houbysoft
@houbysoft: Je suis content que cela ait fonctionné pour vous. unbuffern'est qu'un petit script, vous n'auriez donc pas dû avoir besoin de recompiler l'ensemble du paquet.
Suspendu jusqu'à nouvel ordre.
Ouais, probablement pas, mais cela a ./configure && makepris environ 10 secondes, puis je suis passé unbufferà /usr/local/bin:)
houbysoft
3
Je l'ai installé sur mon mac (10.8.5) via brew: brew install expect --with-brewed-tk
Nils
2
FWIW, parce que la suppression du tampon est quelque peu déroutante, la structure pertinente est unbuffer {commands with pipes/tee}.
Fake Name
128

tu peux essayer stdbuf

$ stdbuf -o 0 ./a | tee output.txt

(grande) partie de la page de manuel:

  -i, --input=MODE   adjust standard input stream buffering
  -o, --output=MODE  adjust standard output stream buffering
  -e, --error=MODE   adjust standard error stream buffering

If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.

If MODE is '0' the corresponding stream will be unbuffered.

Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

gardez cela à l'esprit, cependant:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

vous n'êtes pas en cours d' exécution stdbufsur tee, vous utilisez sur a, donc cela ne devrait pas vous affecter, à moins que vous définissez la mise en mémoire tampon des a« cours d'eau de dans a» la source.

De plus, ce stdbufn'est pas POSIX, mais fait partie de GNU-coreutils.

c00kiemon5ter
la source
3
Merci, mais cela ne semble pas être disponible sur OS X (la question est étiquetée osx-lion).
houbysoft
2
@houbysoft - Je suis presque sûr que les outils GNU peuvent être installés sur OS X
jordanm
1
@jordanm: peut-être, mais installer l'ensemble des outils GNU semble un peu exagéré pour cela ...
houbysoft
1
Nous avons voté pour cette réponse car elle stdbufest déjà disponible sur les distributions Linux Centos que nous utilisons et unbufferne l'est pas. Merci!
Huw Walters le
6
Pour le script python, stdbuf ne fonctionnera pas, mais vous pouvez utiliser -upour désactiver la mise en mémoire tampon du côté de python:python3 -u a.py | tee output.txt
Honza le
27

Vous pouvez également essayer d'exécuter votre commande dans un pseudo-terminal à l'aide de la scriptcommande (qui devrait appliquer la sortie tamponnée en ligne au tube)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

Sachez que la scriptcommande ne propage pas le statut de sortie de la commande encapsulée.

Jon
la source
3
script -t 1 /path/to/outputfile.txt ./aa très bien fonctionné pour mon cas d'utilisation. Il diffuse en direct toutes les sorties outputfile.txttout en les imprimant sur la sortie standard de votre shell. N'a pas besoin d'utilisertee
Peter Berg
26

Vous pouvez utiliser setlinebuf depuis stdio.h.

setlinebuf(stdout);

Cela devrait changer la mise en mémoire tampon en "ligne tamponnée".

Si vous avez besoin de plus de flexibilité, vous pouvez utiliser setvbuf.

Denys Rtveliashvili
la source
8
Je me demande pourquoi cette solution a si peu de votes positifs. C'est la seule solution qui n'impose pas de charge à l'appelant.
oxygène
1
Notez que ce n'est pas le C standard (ni même POSIX). Il est probablement préférable d'utiliser setvbuf(stdout, NULL, _IOLBF, 0), ce qui est exactement équivalent.
rvighne
Cela a résolu mon problème sur OS X Catalina avec un programme C ++ qui était printf () ing et je faisais un piping au tee mais je ne voyais la sortie que lorsque le programme était terminé.
jbaxter le
2

Si vous utilisez les classes de flux C ++ à la place, every std::endl est un flush implicite. En utilisant l'impression de style C, je pense que la méthode que vous avez suggérée ( fflush()) est le seul moyen.

Kevin Grant
la source
4
Malheureusement, ce n'est pas vrai. Vous pouvez observer le même comportement avec c ++ std :: cout même lorsque vous utilisez std :: endl ou std :: flush. La mise en mémoire tampon se produit par dessus et la solution la plus simple sous Linux semble être setlinebuf (stdout); comme la toute première ligne de main () lorsque vous êtes l'auteur du programme et que vous utilisez les autres solutions ci-dessus lorsque vous ne pouvez pas changer le code source.
oxygène
1
@oxygene Ce n'est pas vrai. Je l'ai essayé et endl rince le tampon lors du raccordement au tee (contrairement à printf). Code: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl vide toujours le tampon comme défini ici: en.cppreference.com/w/cpp/io/manip/endl
Curtis Yallop