Як перенаправити stdout як на файл, так і на консоль за допомогою сценаріїв?


79

Я хочу запустити скрипт python і захопити висновок у текстовому файлі, а також показати на консолі.

Я хочу вказати це як властивість самого скрипта python. НЕ використовувати команду echo "hello world" | tee test.txtв командному рядку щоразу.

У сценарії я спробував:

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

Але це не відображає вихідні дані на екрані.

Я чув про модуль реєстрації, але не міг пощастити, використовуючи цей модуль для виконання роботи.

Відповіді:


131

Ви можете використовувати перенаправлення оболонки під час виконання файлу python:

python foo_bar.py > file

Це запише всі результати, які будуть надруковані на stdout з джерела python у файл до журналу.

Або якщо ви хочете вести журнал із сценарію:

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("logfile.log", "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.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()

Тепер ви можете використовувати:

print "Hello"

Це напише "Привіт" як у stdout, так і у файл журналу


1
Привіт Аміт, я не хочу використовувати це, оскільки для цього потрібна взаємодія вручну (> файл). Чи є щось, що я можу зробити з допомогою сценарію, або після завершення виконання, що коли-небудь з'явилося на консолі, бере і натискає на файл?
user2033758

@ user2033758: Після виходу з вашої програми та на консолі після завершення виконання програма більше не контролює її.
Антон

1
@ user2033758: У цій відповіді є дві пропозиції, а друга не потребує жодної взаємодії вручну. Ви можете використовувати командний рядок або використовувати клас у своєму коді. Я перевірив це, і клас надсилає вихід як у консоль, так і у файл, без особливих параметрів у командному рядку.
JDM

Як повернути нормальну sys.stdoutповедінку? Скажімо, я хочу це, але лише на старті запуску моєї програми, а потім через деякий час я хочу просто дозволити програмі працювати, але не зберігаючи все
Токе Форбі

@TokeFaurbyВи можете зберегти попереднє sys.stdoutв Logger і створити функцію, яка повертає його до початкового стану.
Gábor Fekete

20

Я отримав спосіб одночасно перенаправити вихід, викладений на консоль, а також на текстовий файл:

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)

15

Використовуйте модуль реєстрації для налагодження та стеження за вашим додатком

Ось як мені вдалося увійти до файлу та до console / stdout

import logging
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    filename='logs_file',
                    filemode='w')
# Until here logs only to file: 'logs_file'

# define a new Handler to log to console as well
console = logging.StreamHandler()
# optional, set the logging level
console.setLevel(logging.INFO)
# set a format which is the same for console use
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

# Now, we can log to both ti file and console
logging.info('Jackdaws love my big sphinx of quartz.')
logging.info('Hello world')

прочитайте з джерела: https://docs.python.org/2/howto/logging-cookbook.html


10

Я придумав більш просте рішення. Просто визначте функцію, яка буде надрукована у файл, на екран або на обидва з них. У наведеному нижче прикладі я дозволяю користувачеві вводити ім'я вихідного файлу як аргумент, але це не є обов'язковим:

OutputFile= args.Output_File
OF = open(OutputFile, 'w')

def printing(text):
    print text
    if args.Output_File:
        OF.write(text + "\n")

Після цього для друку рядка як у файл, так і / або на екран потрібно лише: друк (Line_to_be_printed)


1
Проста і блискуча ідея впоратися з нею за допомогою спеціальної функції.
Акіф

1
Це працює. Однак зверніть увагу, що функція введення тексту в текст для друку повинна бути явним текстом. Він не підтримуватиме жодних додаткових аргументів, які підтримує друк.
rrlamichhane

Моє питання підтримки невідвертого тексту (наприклад, datetime.datetime) було у функції запису. Я просто оточив текст str(text)для перетворення в рядок.
тзг

10

На основі відповіді Аміт Куялджі , ось простий модуль, який ви можете використовувати для ведення журналу -

transcript.py:

"""
Transcript - direct print output to a file, in addition to terminal.

Usage:
    import transcript
    transcript.start('logfile.log')
    print("inside file")
    transcript.stop()
    print("outside file")
"""

import sys

class Transcript(object):

    def __init__(self, filename):
        self.terminal = sys.stdout
        self.logfile = open(filename, "a")

    def write(self, message):
        self.terminal.write(message)
        self.logfile.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

def start(filename):
    """Start transcript, appending print output to given filename"""
    sys.stdout = Transcript(filename)

def stop():
    """Stop transcript and return print functionality to normal"""
    sys.stdout.logfile.close()
    sys.stdout = sys.stdout.terminal

7
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.

TeeКлас IPython.utils.ioробить те , що ви хочете, але це не вистачає __enter__і __exit__методи , необхідні для виклику його в with-statement. Їх додає contextlib.closing.


5

Я спробував тут кілька рішень і не знайшов того, який одночасно пише у файл та в консоль. Отже, ось що я зробив (на основі цієї відповіді)

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()   

Це рішення використовує більшу обчислювальну потужність, але надійно зберігає всі дані зі stdout у файл реєстратора та використовує менше пам'яті. Для своїх потреб я також додав позначку часу в self.log.write (повідомлення). Чудово працює.


3

Ось простий диспетчер контексту, який друкує на консолі і записує той самий результат у файл. Він також записує будь-які винятки у файл.

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()

Щоб скористатися менеджером контексту:

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

Хороша відповідь! У Python3 я отримав помилку через відсутність "isatty". Щоб це виправити, додайте наступне: def isatty (self): return False
matt3o

1

Щоб перенаправити вихід у файл і термінал, не змінюючи способу використання сценарію Python зовні, ви можете використовувати 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)

Якщо ptyмодуль недоступний (у Windows), ви можете замінити його teed_call()функцією, яка є більш портативною, але він надає звичайні канали замість псевдотермінала - це може змінити поведінку деяких програм.

Перевага pty.spawnі subprocess.Popen-О рішення над заміною sys.stdoutз файл-подібний об'єкт , який вони можуть захопити вихід на дескриптор файлу рівень , наприклад, якщо скрипт запускає інші процеси , які також можуть виробляти висновок на стандартний висновок / STDERR. Дивіться мою відповідь на відповідне питання: Перенаправити stdout на файл у Python?


0

На основі відредагованої відповіді Брайана Бернса я створив єдиний клас, який простіше назвати:

class Logger(object):

    """
    Class to log output of the command line to a log file

    Usage:
    log = Logger('logfile.log')
    print("inside file")
    log.stop()
    print("outside file")
    log.start()
    print("inside again")
    log.stop()
    """

    def __init__(self, filename):
        self.filename = filename

    class Transcript:
        def __init__(self, filename):
            self.terminal = sys.stdout
            self.log = open(filename, "a")
        def __getattr__(self, attr):
            return getattr(self.terminal, attr)
        def write(self, message):
            self.terminal.write(message)
            self.log.write(message)
        def flush(self):
            pass

    def start(self):
        sys.stdout = self.Transcript(self.filename)

    def stop(self):
        sys.stdout.log.close()
        sys.stdout = sys.stdout.terminal

-2

Я спробував це:

"""
Transcript - direct print output to a file, in addition to terminal.

Usage:
    import transcript
    transcript.start('logfile.log')
    print("inside file")
    transcript.stop()
    print("outside file")
"""

import sys

class Transcript(object):

    def __init__(self, filename):
        self.terminal = sys.stdout, sys.stderr
        self.logfile = open(filename, "a")

    def write(self, message):
        self.terminal.write(message)
        self.logfile.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

def start(filename):
    """Start transcript, appending print output to given filename"""
    sys.stdout = Transcript(filename)

def stop():
    """Stop transcript and return print functionality to normal"""
    sys.stdout.logfile.close()
    sys.stdout = sys.stdout.terminal
    sys.stderr = sys.stderr.terminal

-5

Ви можете перенаправити висновок у файл, використовуючи >> python із синтаксисом "chevron" print rint, як зазначено в документах

Подивимося,

fp=open('test.log','a')   # take file  object reference 
print >> fp , "hello world"            #use file object with in print statement.
print >> fp , "every thing will redirect to file "
fp.close()    #close the file 

замовте файл test.log, у вас будуть дані, а для друку на консолі просто використовуйте звичайний оператор друку .


1
>>не є оператором перенаправлення, це оператором зміни. Змініть це, будь ласка.
nbro

Це справедливо в python2.7. >> може використовуватися з оператором print для перенаправлення рядка на даний об'єкт файлу. Будь ласка, перевірте, спробувавши моє рішення самостійно
neotam
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.