A partir du stdout
page de manuel :
Le flux stderr n'est pas mis en mémoire tampon.Le flux stdout est mis en mémoire tampon en lignelorsqu'il pointe vers un terminal .Les lignes partielles n'apparaîtront pas jusqu'à ce que fflush(3) ou exit(3) soit appelé, ou qu'une nouvelle ligne soit imprimée.
Conclusion :à moins que la sortie ne soit un terminal, votre programme aura sa sortie standard en mode entièrement tamponné par défaut. Cela signifie essentiellement qu'il affichera les données dans des blocs de grande taille, plutôt que ligne par ligne, et encore moins caractère par caractère.
Façons de contourner ce problème :
-
Corrigez votre programme :si vous avez besoin d'une sortie en temps réel, vous devez corriger votre programme. En C, vous pouvez utiliser
fflush(stdout)
après chaque instruction de sortie, ousetvbuf()
pour changer le mode tampon de la sortie standard. Pour Python, il y asys.stdout.flush()
de même certaines des suggestions ici. -
Utilisez un utilitaire qui peut enregistrer à partir d'un PTY, plutôt que des redirections pures et simples. GNU Screen peut le faire pour vous :
screen -d -m -L python test.py
serait un début. Cela enregistrera la sortie de votre programme dans un fichier appelé
screenlog.0
(ou similaire) dans votre répertoire actuel avec un délai par défaut de 10 secondes, et vous pouvez utiliserscreen
pour vous connecter à la session où votre commande est en cours d'exécution pour fournir une entrée ou y mettre fin. Le délai et le nom du fichier journal peuvent être modifiés dans un fichier de configuration ou manuellement une fois que vous vous connectez à la session en arrière-plan.
MODIFIER :
Sur la plupart des systèmes Linux, il existe une troisième solution :vous pouvez utiliser le LD_PRELOAD
variable et une bibliothèque préchargée pour remplacer certaines fonctions de la bibliothèque C et les utiliser pour définir le stdout
mode tampon lorsque ces fonctions sont appelées par votre programme. Cette méthode peut fonctionner, mais elle présente un certain nombre d'inconvénients :
-
Cela ne fonctionnera pas du tout sur les exécutables statiques
-
C'est fragile et plutôt moche.
-
Cela ne fonctionnera pas du tout avec les exécutables SUID - le chargeur dynamique refusera de lire le
LD_PRELOAD
variable lors du chargement de tels exécutables pour des raisons de sécurité. -
C'est fragile et plutôt moche.
-
Cela nécessite que vous trouviez et redéfinissiez une fonction de bibliothèque appelée par votre programme après il définit initialement le
stdout
mode tampon et de préférence avant toute sortie.getenv()
est un bon choix pour de nombreux programmes, mais pas pour tous. Vous devrez peut-être remplacer les fonctions d'E/S courantes telles queprintf()
oufwrite()
- si le push vient à bout, vous devrez peut-être remplacer toutes les fonctions qui contrôlent le mode de mise en mémoire tampon et introduire une condition spéciale pourstdout
. -
C'est fragile et plutôt moche.
-
Il est difficile de s'assurer qu'il n'y a pas d'effets secondaires indésirables. Pour faire cela correctement, vous devez vous assurer que seul
stdout
est affecté et que vos remplacements ne planteront pas le reste du programme si par ex.stdout
est fermé. -
Ai-je mentionné qu'il est fragile et plutôt moche ?
Cela dit, le processus est relativement simple. Vous mettez un fichier C, par ex. linebufferedstdout.c
les fonctions de remplacement :
#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);
}
Ensuite, vous compilez ce fichier en tant qu'objet partagé :
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Ensuite, vous définissez le LD_PRELOAD
variable pour la charger avec votre programme :
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
Si vous avez de la chance, votre problème sera résolu sans malheureux effets secondaires.
Vous pouvez définir le LD_PRELOAD
bibliothèque dans le shell, si nécessaire, ou même spécifier cette bibliothèque à l'échelle du système (certainement PAS recommandé) en /etc/ld.so.preload
.
Avez-vous pensé à raccorder le raccord en T ?
./program | tee a.txt
Cependant, même tee ne fonctionnera pas si "program" n'écrit rien sur stdout jusqu'à ce qu'il soit terminé. Ainsi, l'efficacité dépend beaucoup du comportement de votre programme.
Si vous essayez de modifier le comportement d'un programme existant, essayez stdbuf (partie de coreutils à partir de la version 7.5 apparemment).
Ceci tamponne stdout jusqu'à une ligne :
stdbuf -oL command > output
Cela désactive complètement la mise en mémoire tampon stdout :
stdbuf -o0 command > output