web-dev-qa-db-fra.com

Ecrire Python stdout dans un fichier immédiatement

Lorsque vous essayez d'écrire la sortie standard d'un script Python dans un fichier texte (python script.py > log), le fichier texte est créé au démarrage de la commande, mais le contenu réel n'est écrit qu'à la fin du script Python. Par exemple:

script.py:

import time
for i in range(10):
    print('bla')
    time.sleep(5)

imprime sur stdout toutes les 5 secondes lorsqu'il est appelé avec python script.py, mais quand j'appelle python script.py > log, la taille du fichier journal reste nulle jusqu'à la fin du script. Est-il possible d'écrire directement dans le fichier journal, de manière à pouvoir suivre la progression du script (par exemple en utilisant tail)?

MODIFIER Il s'avère que python -u script.py fait l'affaire, je ne connaissais pas la mise en mémoire tampon de stdout.

60
Bart

Cela se produit car normalement lorsque le processus STDOUT est redirigé vers autre chose qu'un terminal, la sortie est mise en mémoire tampon dans un tampon de taille spécifique au système d'exploitation (peut-être 4k ou 8k dans de nombreux cas). Inversement, lors de la sortie vers un terminal, STDOUT sera mis en mémoire tampon de ligne ou pas du tout, donc vous verrez la sortie après chaque \n Ou pour chaque caractère.

Vous pouvez généralement modifier la mise en mémoire tampon STDOUT avec l'utilitaire stdbuf :

stdbuf -oL python script.py > log

Maintenant, si vous tail -F log, Vous devriez voir chaque sortie de ligne immédiatement lorsqu'elle est générée.


Alternativement, un rinçage explicite du flux de sortie après chaque impression devrait obtenir le même résultat. Il semble que sys.stdout.flush() devrait y parvenir en Python. Si vous utilisez Python 3.3 ou plus récent, la fonction print possède également un mot clé flush qui fait ceci: print('hello', flush=True).

70
Digital Trauma

Cela devrait faire le travail:

import time, sys
for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)

Comme Python tamponnera le stdout par défaut, ici j'ai utilisé sys.stdout.flush() pour vider le tampon.

Une autre solution serait d'utiliser le commutateur -u (Sans tampon) de python. Donc, ce qui suit fera aussi:

python -u script.py >> log
47
heemayl

Une variation sur le thème de l'utilisation de la propre option de python pour une sortie sans tampon serait d'utiliser #!/usr/bin/python -u comme première ligne.

Avec #!/usr/bin/env python cet argument supplémentaire ne fonctionnera pas, donc alternativement, on pourrait exécuter PYTHONUNBUFFERED=1 ./my_scriipt.py > output.txt ou faites-le en deux étapes:

$ export PYTHONUNBUFFERED=1
$ ./myscript.py
14

Vous devez réussir flush=True à la fonction print :

import time

for i in range(10):
    print('bla', flush=True)
    time.sleep(5)

Selon la documentation, par défaut, print n'applique rien sur le vidage:

La sortie du tampon est généralement déterminée par fichier, mais si l'argument de mot clé flush est vrai, le flux est vidé de force.

Et la documentation des strems de sys dit:

Lorsqu'ils sont interactifs, les flux standard sont tamponnés en ligne. Sinon, ils sont mis en mémoire tampon comme des fichiers texte normaux. Vous pouvez remplacer cette valeur avec le -u option de ligne de commande.


Si vous êtes coincé avec une ancienne version de python vous devez appeler la méthode flush de la sys.stdout stream:

import sys
import time

for i in range(10):
    print('bla')
    sys.stdout.flush()
    time.sleep(5)
12
Bakuriu