如何通过脚本将标准输出重定向到文件和控制台?


79

我想运行一个python脚本并捕获文本文件上的输出以及想在控制台上显示。

我想将其指定为python脚本本身的属性。不要echo "hello world" | tee test.txt每次都在命令提示符下使用该命令。

在脚本中,我尝试过:

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

但这不会在屏幕上显示stdout输出。

我听说过有关日志记录模块的信息,但是使用该模块完成这项工作我不走运。

Answers:


130

您可以在执行python文件时使用shell重定向:

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"

这会将“ Hello”写入标准输出和日志文件


1
嗨,Amith,我不想使用它,因为它需要手动交互才能执行此操作(>文件)。我还有什么可以在脚本中使用的,或者一旦执行完成就可以在控制台上进行处理,获取并推送到文件中吗?
user2033758 2013年

@ user2033758:一旦程序退出程序,并且在控制台上执行完成,该程序将无法对其进行任何控制。
Anthon

1
@ user2033758:此答案中有两个建议,第二个不需要任何手动交互。您可以使用命令行,也可以在代码中使用类。我对此进行了测试,该类将输出发送到控制台和文件,而命令行上没有任何特殊参数。
JDM

如何恢复正常sys.stdout行为?说我想要这个,但是只在程序的启动阶段,然后过一会儿我想让程序运行,但是不保存所有内容
Toke Faurby

@TokeFaurby您可以将以前的版本保存sys.stdout在Logger中,并创建一个将其恢复到原始状态的函数。
加博尔·费克特(GáborFekete)

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

使用日志记录模块调试和关注您的应用

这是我设法登录文件和控制台/标准输出的方法

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

此后,将行同时打印到文件和/或屏幕所需的所有操作是:printing(Line_to_be_printed)


1
使用自定义函数来处理它的简单而出色的想法。
阿基夫(Afif)

1
这可行。但是,请注意,“打印”功能的输入“文本”必须是显式文本。它不支持print支持的任何其他参数。
rrlamichhane '18

我支持非显式文本(例如datetime.datetime)的问题是在write函数中。我只是str(text)将文本括起来以转换为字符串。
tzg

10

根据Amith Koujalgi的回答,这是一个可用于记录日志的简单模块-

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执行您想要的操作,但是缺少在-statement中调用它所需的__enter____exit__方法with。这些是由添加的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()   

该解决方案使用更多的计算能力,但可靠地将所有数据从标准输出保存到记录器文件中,并使用较少的内存。为了我的需要,我也将时间戳添加到self.log.write(message)中。效果很好。


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.spawnsubprocess.Popen的解决方案优于用sys.stdout类似文件的对象替代的优点是,它们可以捕获文件描述符级别的输出,例如,如果脚本启动了其他进程,这些进程也可以在stdout / stderr上产生输出。请参阅我对相关问题的回答:将stdout重定向到Python中的文件?


0

基于Brian Burns编辑的答案,我创建了一个易于调用的类:

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和print rint的“ chevron”语法将输出重定向到文件,如文档所示

让我们看看,

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.