103 votes

Comment rediriger stdout à la fois vers un fichier et vers la console avec un script ?

Je veux exécuter un script de python et capturer la sortie sur un fichier texte ainsi que vouloir montrer sur la console.

Je veux le spécifier comme une propriété du script python lui-même. PAS pour utiliser la commande echo "hello world" | tee test.txt à l'invite de commande à chaque fois.

Dans script j'ai essayé :

sys.stdout = open('log.txt','w')

Mais cela n'affiche pas la sortie stdout à l'écran.

J'ai entendu parler du module de journalisation mais je n'ai pas eu de chance en utilisant ce module pour faire le travail.

9voto

oha Points 121
from IPython.utils.io import Tee
from contextlib import closing

print('This is not in the output file.')        

with closing(Tee("outputfile.log", "w", channel="stdout")) as outputstream:
    print('This is written to the output file and the console.')
    # raise Exception('The file "outputfile.log" is closed anyway.')
print('This is not written to the output file.')   

# Output on console:
# This is not in the output file.
# This is written to the output file and the console.
# This is not written to the output file.

# Content of file outputfile.txt:
# This is written to the output file and the console.

En Tee classe dans IPython.utils.io fait ce que vous voulez, mais il manque le __enter__ y __exit__ nécessaires pour l'appeler dans le with -déclaration. Ceux-ci sont ajoutés par contextlib.closing .

5voto

supersolver Points 346

Voici un gestionnaire de contexte simple qui imprime sur la console et écrit la même sortie dans un fichier. Il écrit également toutes les exceptions dans le fichier.

import traceback
import sys

# Context manager that copies stdout and any exceptions to a log file
class Tee(object):
    def __init__(self, filename):
        self.file = open(filename, 'w')
        self.stdout = sys.stdout

    def __enter__(self):
        sys.stdout = self

    def __exit__(self, exc_type, exc_value, tb):
        sys.stdout = self.stdout
        if exc_type is not None:
            self.file.write(traceback.format_exc())
        self.file.close()

    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)

    def flush(self):
        self.file.flush()
        self.stdout.flush()

Pour utiliser le gestionnaire de contexte :

print("Print")
with Tee('test.txt'):
    print("Print+Write")
    raise Exception("Test")
print("Print")

5voto

Sanon Selkov Points 73

J'ai essayé plusieurs solutions ici et je n'ai pas trouvé celle qui écrit dans le fichier et dans la console en même temps. Voici donc ce que j'ai fait (sur la base de cette réponse)

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout

    def write(self, message):
        with open ("logfile.log", "a", encoding = 'utf-8') as self.log:            
            self.log.write(message)
        self.terminal.write(message)

    def flush(self):
        #this flush method is needed for python 3 compatibility.
        #this handles the flush command by doing nothing.
        #you might want to specify some extra behavior here.
        pass
sys.stdout = Logger()   

Cette solution utilise plus de puissance de calcul, mais enregistre de manière fiable toutes les données de stdout dans le fichier de journalisation et utilise moins de mémoire. Pour mes besoins, j'ai également ajouté l'horodatage dans self.log.write(message). Cela fonctionne très bien.

4voto

Arnold Souza Points 196

Cette méthode a très bien fonctionné dans ma situation. J'ai juste ajouté quelques modifications basées sur d'autres codes présentés dans ce fil.

import sys, os 

orig_stdout = sys.stdout  # capture original state of stdout

te = open('log.txt','w')  # File where you need to keep the logs

class Unbuffered:
   def __init__(self, stream):
       self.stream = stream

   def write(self, data):
       self.stream.write(data)
       self.stream.flush()
       te.write(data)    # Write the data of stdout here to a text file as well

sys.stdout=Unbuffered(sys.stdout)

#######################################
##  Feel free to use print function  ##
#######################################

print("Here is an Example =)")

#######################################
##  Feel free to use print function  ##
#######################################

# Stop capturing printouts of the application from Windows CMD
sys.stdout = orig_stdout  # put back the original state of stdout
te.flush()  # forces python to write to file
te.close()  # closes the log file

# read all lines at once and capture it to the variable named sys_prints
with open('log.txt', 'r+') as file:
    sys_prints = file.readlines() 

# erase the file contents of log file
open('log.txt', 'w').close()

1voto

J.F. Sebastian Points 102961

Pour rediriger la sortie vers un fichier et un terminal sans modifier la manière dont votre script Python est utilisé à l'extérieur, vous pourriez utiliser pty.spawn(itself) :

#!/usr/bin/env python
"""Redirect stdout to a file and a terminal inside a script."""
import os
import pty
import sys

def main():
    print('put your code here')

if __name__=="__main__":
    sentinel_option = '--dont-spawn'
    if sentinel_option not in sys.argv:
        # run itself copying output to the log file
        with open('script.log', 'wb') as log_file:
            def read(fd):
                data = os.read(fd, 1024)
                log_file.write(data)
                return data

            argv = [sys.executable] + sys.argv + [sentinel_option]
            rc = pty.spawn(argv, read)
    else:
        sys.argv.remove(sentinel_option)
        rc = main()
    sys.exit(rc)

Si pty n'est pas disponible (sous Windows), alors vous pouvez le remplacer par teed_call() fonction qui est plus portable mais qui fournit des pipes ordinaires au lieu d'un pseudo-terminal -- cela peut changer le comportement de certains programmes.

L'avantage de pty.spawn y subprocess.Popen -plutôt que de remplacer sys.stdout avec un objet de type fichier est qu'ils peuvent capturer la sortie au niveau d'un descripteur de fichier, par exemple, si le script démarre d'autres processus qui peuvent également produire une sortie sur stdout/stderr. Voir ma réponse à la question connexe : Rediriger stdout vers un fichier en Python ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X