GNU/Linux >> Tutoriels Linux >  >> Linux

redirection de sortie à la volée, voir la sortie de redirection de fichier pendant que le programme est toujours en cours d'exécution

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, ou setvbuf() pour changer le mode tampon de la sortie standard. Pour Python, il y a sys.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 utiliser screen 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 que printf() ou fwrite() - 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 pour stdout .

  • 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


Linux
  1. Lire et écrire des données de n'importe où avec la redirection dans le terminal Linux

  2. Apprenez les bases du fonctionnement de la redirection Linux I/O (Entrée/Sortie)

  3. Comment rediriger la sortie vers un fichier et Stdout sous Linux

  4. Comment rediriger la sortie d'un programme vers un fichier Zip ? ?

  5. Sortie vers Stdout et en même temps Grep dans un fichier ?

Comment rediriger la sortie vers un fichier et stdout

Comment ajouter la sortie à un fichier ?

afficher la 2ème colonne d'un fichier

Rediriger toutes les sorties vers un fichier dans Bash

Comment rediriger la sortie de system() vers un fichier ?

Déterminez si la sortie est stdout ou stderr