Del stdout
página de manual:
El flujo estándar stderr no tiene búfer. El flujo estándar stdout tiene un búfer de línea cuando apunta a una terminal .Las líneas parciales no aparecerán hasta que se llame fflush(3) o exit(3), o se imprima una nueva línea.
En pocas palabras:a menos que la salida sea una terminal, su programa tendrá su salida estándar en modo de búfer completo de forma predeterminada. Básicamente, esto significa que generará datos en bloques grandes, en lugar de línea por línea, y mucho menos carácter por carácter.
Maneras de evitar esto:
-
Arregle su programa:si necesita una salida en tiempo real, necesita arreglar su programa. En C puedes usar
fflush(stdout)
después de cada declaración de salida, osetvbuf()
para cambiar el modo de almacenamiento en búfer de la salida estándar. Para Python haysys.stdout.flush()
de incluso algunas de las sugerencias aquí. -
Use una utilidad que pueda grabar desde un PTY, en lugar de redirecciones directas de salida estándar. GNU Screen puede hacer esto por usted:
screen -d -m -L python test.py
sería un comienzo. Esto registrará la salida de su programa en un archivo llamado
screenlog.0
(o similar) en su directorio actual con un retraso predeterminado de 10 segundos, y puede usarscreen
para conectarse a la sesión donde se está ejecutando su comando para proporcionar una entrada o terminarla. El retraso y el nombre del archivo de registro se pueden cambiar en un archivo de configuración o manualmente una vez que se conecte a la sesión en segundo plano.
EDITAR:
En la mayoría de los sistemas Linux, existe una tercera solución alternativa:puede usar el LD_PRELOAD
variable y una biblioteca precargada para anular las funciones seleccionadas de la biblioteca C y usarlas para establecer el stdout
modo de almacenamiento en búfer cuando esas funciones son llamadas por su programa. Este método puede funcionar, pero tiene una serie de desventajas:
-
No funcionará en absoluto en ejecutables estáticos
-
Es frágil y bastante feo.
-
No funcionará en absoluto con los ejecutables SUID:el cargador dinámico se negará a leer el
LD_PRELOAD
variable al cargar tales ejecutables por razones de seguridad. -
Es frágil y bastante feo.
-
Requiere que encuentre y anule una función de biblioteca a la que llama su programa después inicialmente establece el
stdout
modo de almacenamiento en búfer y preferiblemente antes cualquier salida.getenv()
es una buena opción para muchos programas, pero no para todos. Es posible que deba anular funciones de E/S comunes comoprintf()
ofwrite()
- si llega el momento, es posible que deba anular todas las funciones que controlan el modo de almacenamiento en búfer e introducir una condición especial parastdout
. -
Es frágil y bastante feo.
-
Es difícil asegurarse de que no haya efectos secundarios no deseados. Para hacer esto correctamente, debe asegurarse de que solo
stdout
se ve afectado y que sus anulaciones no bloquearán el resto del programa si, p.stdout
está cerrado. -
¿Mencioné que es frágil y bastante feo?
Dicho esto, el proceso es relativamente simple. Pones un archivo C, p. linebufferedstdout.c
las funciones de reemplazo:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
char *getenv(const char *s) {
static char *(*getenv_real)(const char *s) = NULL;
if (getenv_real == NULL) {
getenv_real = dlsym(RTLD_NEXT, "getenv");
setlinebuf(stdout);
}
return getenv_real(s);
}
Luego compilas ese archivo como un objeto compartido:
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Luego configuras el LD_PRELOAD
variable para cargarlo junto con su programa:
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
Si tiene suerte, su problema se resolverá sin desafortunados efectos secundarios.
Puede establecer el LD_PRELOAD
biblioteca en el shell, si es necesario, o incluso especificar esa biblioteca en todo el sistema (definitivamente NO recomendado) en /etc/ld.so.preload
.
¿Has pensado en hacer ribetes en T?
./program | tee a.txt
Sin embargo, incluso tee no funcionará si el "programa" no escribe nada en stdout hasta que esté listo. Entonces, la efectividad depende mucho de cómo se comporte su programa.
Si está tratando de modificar el comportamiento de un programa existente, pruebe con stdbuf (aparentemente parte de coreutils que comienza con la versión 7.5).
Esto almacena la salida estándar hasta una línea:
stdbuf -oL command > output
Esto deshabilita el almacenamiento en búfer estándar por completo:
stdbuf -o0 command > output