Forzar el almacenamiento en búfer de línea de la salida estándar al conectar la tubería a la T

117

Por lo general, stdouttiene búfer de línea. En otras palabras, siempre que su printfargumento termine con una nueva línea, puede esperar que la línea se imprima instantáneamente. Esto no parece mantenerse cuando se usa una tubería para redireccionar tee.

Tengo un programa C ++ a, que genera cadenas, siempre \nterminadas, a stdout.

Cuando se ejecuta solo ( ./a), todo se imprime correctamente y en el momento adecuado, como se esperaba. Sin embargo, si lo canalizo a tee( ./a | tee output.txt), no imprime nada hasta que se cierra, lo que anula el propósito de usar tee.

Sé que podría solucionarlo agregando un fflush(stdout)después de cada operación de impresión en el programa C ++. Pero, ¿existe una forma más limpia y sencilla? ¿Hay algún comando que pueda ejecutar, por ejemplo, que obligaría stdouta tener búfer de línea, incluso cuando se usa una tubería?

houbysoft
fuente

Respuestas:

67

Prueba unbuffercuál es parte del expectpaquete. Es posible que ya lo tenga en su sistema.

En su caso, lo usaría así:

./a | unbuffer -p tee output.txt

( -pes para el modo de canalización donde unbuffer lee de stdin y lo pasa al comando en el resto de los argumentos)

Pausado hasta nuevo aviso.
fuente
Gracias, esto funcionó, aunque tuve que compilarme expectya unbufferque no parece estar incluido de forma predeterminada en OS X.
houbysoft
@houbysoft: Me alegro de que te haya funcionado. unbufferes solo un pequeño script, por lo que no debería haber tenido que volver a compilar todo el paquete.
Pausado hasta nuevo aviso.
Sí, probablemente no, pero me ./configure && maketomó unos 10 segundos y luego me mudé unbuffera /usr/local/bin:)
houbysoft
3
Lo instalé en mi mac (10.8.5) a través de brew: brew install esperan --with-brewed-tk
Nils
2
FWIW, debido a que unbuffer es algo confuso, la estructura relevante lo es unbuffer {commands with pipes/tee}.
Nombre falso
128

puedes probar stdbuf

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

(gran) parte de la página de manual:

  -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.

Sin embargo, tenga esto en cuenta:

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.

no se está ejecutando stdbufen tee, que se está ejecutando en a, por lo que esto no debe afectar, a menos que establezca el búfer de a's corrientes en a' fuente s.

Además, nostdbuf es POSIX, sino parte de GNU-coreutils.

c00kiemon5ter
fuente
3
Gracias, pero esto no parece estar disponible en OS X (la pregunta está etiquetada como osx-lion).
houbysoft
2
@houbysoft - Estoy bastante seguro de que las herramientas GNU se pueden instalar en OS X
jordanm
1
@jordanm: quizás, pero instalar todas las herramientas GNU parece una exageración para esto ...
houbysoft
1
Voto a favor esta respuesta porque stdbufya está disponible en las distribuciones de Centos Linux que estamos usando, y unbufferno lo está. ¡Gracias!
Huw Walters
6
Para el script de Python, stdbuf no funcionará, pero puede usarlo -upara deshabilitar el almacenamiento en búfer en el lado de Python:python3 -u a.py | tee output.txt
Honza
27

¡También puede intentar ejecutar su comando en un pseudo-terminal usando el scriptcomando (que debería hacer cumplir la salida con búfer de línea en la tubería)!

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

Tenga en cuenta que el scriptcomando no propaga el estado de salida del comando empaquetado.

Jon
fuente
3
script -t 1 /path/to/outputfile.txt ./afuncionó muy bien para mi caso de uso. Transmite en vivo toda la salida al mismo outputfile.txttiempo que la imprime en la salida estándar de su shell. No necesitaba usartee
Peter Berg
26

Puede usar setlinebuf desde stdio.h.

setlinebuf(stdout);

Esto debería cambiar el almacenamiento en búfer a "búfer de línea".

Si necesita más flexibilidad, puede usar setvbuf.

Denys Rtveliashvili
fuente
8
Me pregunto por qué esta solución tiene tan pocos votos a favor. Ésta es la única solución que no impone una carga a la persona que llama.
oxygene
1
Tenga en cuenta que esto no es C estándar (o incluso POSIX). Probablemente sea mejor usarlo setvbuf(stdout, NULL, _IOLBF, 0), que es exactamente equivalente.
rvighne
Esto solucionó mi problema en OS X Catalina con un programa de C ++ que estaba imprimiendo y yo estaba funcionando pero solo estaba viendo la salida cuando el programa había terminado.
jbaxter
2

Si usa las clases de flujo de C ++ en su lugar, cada std::endl es un vaciado implícito. Usando la impresión de estilo C, creo que el método que sugirió ( fflush()) es la única forma.

Kevin Grant
fuente
4
Desafortunadamente, esto no es verdad. Puede observar el mismo comportamiento con c ++ std :: cout incluso cuando usa std :: endl o std :: flush. El almacenamiento en búfer ocurre en la parte superior y la solución más sencilla en Linux parece ser setlinebuf (stdout); como la primera línea en main () cuando usted es el autor del programa y usa las otras soluciones anteriores cuando no puede cambiar el código fuente.
oxygene
1
@oxygene Esto no es cierto. Lo probé y endl vacía el búfer cuando se conecta a la T (a diferencia de printf). Código: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl siempre vacía el búfer como se define aquí: en.cppreference.com/w/cpp/io/manip/endl
Curtis Yallop