Dal stdout
pagina di manuale:
Lo stream stderr non è bufferizzato. Lo stream stdout è bufferizzato di rigaquando punta a un terminale .Le righe parziali non appariranno fino a quando non viene chiamato fflush(3) o exit(3), o viene stampata una nuova riga.
In conclusione:a meno che l'output non sia un terminale, il tuo programma avrà il suo output standard in modalità completamente bufferizzata per impostazione predefinita. Ciò significa essenzialmente che produrrà i dati in blocchi di grandi dimensioni, piuttosto che riga per riga, figuriamoci carattere per carattere.
Modi per aggirare questo problema:
-
Correggi il tuo programma:se hai bisogno di output in tempo reale, devi correggere il tuo programma. In C puoi usare
fflush(stdout)
dopo ogni istruzione di output, osetvbuf()
per modificare la modalità di buffering dell'output standard. Per Python c'èsys.stdout.flush()
anche di alcuni dei suggerimenti qui. -
Utilizzare un'utilità in grado di registrare da un PTY, piuttosto che reindirizzamenti stdout diretti. GNU Screen può fare questo per te:
screen -d -m -L python test.py
sarebbe un inizio. Questo registrerà l'output del tuo programma in un file chiamato
screenlog.0
(o simile) nella directory corrente con un ritardo predefinito di 10 secondi e puoi utilizzarescreen
per connettersi alla sessione in cui è in esecuzione il comando per fornire input o terminarlo. Il ritardo e il nome del file di registro possono essere modificati in un file di configurazione o manualmente dopo esserti connesso alla sessione in background.
MODIFICA:
Sulla maggior parte dei sistemi Linux c'è una terza soluzione alternativa:puoi usare il LD_PRELOAD
variabile e una libreria precaricata per sovrascrivere funzioni selezionate della libreria C e utilizzarle per impostare il stdout
modalità di buffering quando tali funzioni vengono chiamate dal programma. Questo metodo può funzionare, ma presenta una serie di svantaggi:
-
Non funzionerà affatto su eseguibili statici
-
È fragile e piuttosto brutto.
-
Non funzionerà affatto con gli eseguibili SUID:il caricatore dinamico si rifiuterà di leggere
LD_PRELOAD
variabile durante il caricamento di tali eseguibili per motivi di sicurezza. -
È fragile e piuttosto brutto.
-
Richiede che tu trovi e sovrascriva una funzione di libreria che viene chiamata dal tuo programma dopo inizialmente imposta il
stdout
modalità buffering e preferibilmente prima qualsiasi uscita.getenv()
è una buona scelta per molti programmi, ma non per tutti. Potrebbe essere necessario eseguire l'override delle comuni funzioni di I/O comeprintf()
ofwrite()
- se arriva il momento critico potresti semplicemente dover sovrascrivere tutte le funzioni che controllano la modalità di buffering e introdurre una condizione speciale perstdout
. -
È fragile e piuttosto brutto.
-
È difficile garantire che non ci siano effetti collaterali indesiderati. Per farlo correttamente dovresti assicurarti che solo
stdout
è interessato e che le tue sostituzioni non bloccheranno il resto del programma se ad es.stdout
è chiuso. -
Ho già detto che è fragile e piuttosto brutto?
Detto questo, il processo è relativamente semplice. Hai inserito un file C, ad es. linebufferedstdout.c
le funzioni di sostituzione:
#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);
}
Quindi compili quel file come oggetto condiviso:
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Quindi imposti il LD_PRELOAD
variabile per caricarla insieme al tuo programma:
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
Se sei fortunato, il tuo problema sarà risolto senza sfortunati effetti collaterali.
Puoi impostare il LD_PRELOAD
libreria nella shell, se necessario, o anche specificare quella libreria a livello di sistema (sicuramente NON consigliato) in /etc/ld.so.preload
.
Hai preso in considerazione il piping to tee?
./program | tee a.txt
Tuttavia, anche tee non funzionerà se "program" non scrive nulla su stdout finché non è terminato. Quindi, l'efficacia dipende molto da come si comporta il tuo programma.
Se stai cercando di modificare il comportamento di un programma esistente, prova stdbuf (apparentemente parte di coreutils a partire dalla versione 7.5).
Questo bufferizza lo stdout fino a una riga:
stdbuf -oL command > output
Questo disabilita del tutto il buffering stdout:
stdbuf -o0 command > output