2012-07-05 7 views
63

Por lo general, stdout tiene memoria intermedia. En otras palabras, siempre que su argumento printf termine con una nueva línea, puede esperar que la línea se imprima instantáneamente. Esto no parece sostenerse cuando se usa un tubo para redirigir al tee.Forzar el almacenamiento en línea de la línea de salida estándar cuando se conecta a la tubería

Tengo un programa C++, a, que emite cadenas, siempre \n -terminated, a stdout.

Cuando se ejecuta solo (./a), todo se imprime correctamente y en el momento correcto, 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, ¿hay una manera más limpia y fácil? ¿Hay algún comando que pueda ejecutar, por ejemplo, que obligue a stdout a ser amortiguado en línea, incluso cuando se usa una tubería?

Respuesta

26

Pruebe unbuffer que forma parte del paquete expect. Es posible que ya lo tenga en su sistema.

+0

Gracias, esto funcionó, aunque tuve que compilar ' expect' yo mismo como 'unbuffer' no parece estar incluido por defecto en OS X. – houbysoft

+0

@houbysoft: me alegro de que funcionó para usted. 'unbuffer' es solo un pequeño script, por lo que no debería haber necesitado recompilar todo el paquete. –

+0

Sí, probablemente no, pero './configure && make' tomó aproximadamente 10 segundos y luego acabo de mover' unbuffer' a '/ usr/local/bin' :) – houbysoft

81

puede probar stdbuf

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

(gran) parte de la página del 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. 

tener esto en cuenta, sin embargo:

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 está ejecutando stdbuf en tee, lo está ejecutando en a, por lo que este no debería afectarlo, a menos que configure el almacenamiento en búfer de las corrientes a en la fuente a.

También, stdbuf es no POSIX, pero parte de GNU-coreutils.

+2

Gracias, pero esto no parece estar disponible en OS X (la pregunta está etiquetada osx-lion). – houbysoft

+1

@houbysoft - Estoy bastante seguro de que se pueden instalar herramientas de GNU en OS X – jordanm

+1

@jordanm: quizás, pero instalar las herramientas GNU completas parece algo exagerado para esto ... – houbysoft

1

Si utiliza las clases de secuencia de C++ en su lugar, cada std::endles una descarga implícita. Con la impresión de estilo C, creo que el método que sugirió (fflush()) es la única manera.

+2

Lamentablemente, esto no es cierto. 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 fácil en Linux parece ser setlinebuf (stdout); como la primera línea en main() cuando usted es el autor del programa y utiliza las otras soluciones anteriores cuando no puede cambiar el código fuente. – oxygene

+0

@oxygene Esto no es verdad. Lo intenté y endl sí limpia el buffer cuando se conecta a la T (a diferencia de printf). Código: '#include #include int main (void) {std :: cout <<" 1 "<< std :: endl; dormir (1); std :: cout << "2" << std :: endl; } ' endl siempre vacía el buffer como se define aquí: http://en.cppreference.com/w/cpp/io/manip/endl –

23

También puede intentar ejecutar su comando en un pseudo-terminal utilizando el comando script (que debería imponer la salida de buffer de línea a 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 el comando script no se propaga hacia atrás el estado de salida del comando envuelta.

+2

'script -t 1 /path/to/outputfile.txt./a' funcionó muy bien para mi caso de uso En vivo transmite todos los resultados a 'archivo de salida.txt' mientras también lo imprime en la salida estándar de su shell. No necesité usar 'tee' –

12

Puede usar setlinebuf desde stdio.h.

setlinebuf(stdout); 

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

Si necesita más flexibilidad, puede usar setvbuf.

+2

Me pregunto por qué esta solución tiene tan pocos votaciones ascendentes. Esta es la única solución que no impone una carga a la persona que llama. – oxygene

Cuestiones relacionadas