如何在Python中将标准输出重定向到任意文件?

When a long-running Python script (e.g, web application) is started from within the ssh session and backgounded, and the ssh session is closed, the application will raise IOError and fail the moment it tries to write to stdout. I needed to find a way to make the application and modules output to a file rather than stdout to prevent failure due to IOError. Currently, I employ nohup to redirect output to a file, and that gets the job done, but I was wondering if there was a way to do it without using nohup, out of curiosity.

我已经试过了。Stdout = open('somefile', 'w'),但这似乎并没有阻止一些外部模块仍然输出到终端(或者sys. exe)。Stdout =…Line根本没有开火)。我知道它应该在我测试过的更简单的脚本上工作,但我还没有时间在web应用程序上进行测试。


当前回答

引用自PEP 343 -“with”语句(添加了import语句):

临时重定向标准输出:

import sys
from contextlib import contextmanager
@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

用途如下:

with open(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"

当然,这不是线程安全的,但手动执行相同的操作也不是。在单线程程序(例如脚本)中,这是一种流行的做事方式。

其他回答

根据这个答案:https://stackoverflow.com/a/5916874/1060344,这是我在我的一个项目中使用的另一种方法。无论你用什么替换sys。Stderr或sys。stderr/ Stdout被用在一些不受你控制的库中,你必须确保替换符合文件接口,特别是当你这样做的时候。该库可能正在使用文件对象的其他方法。

以这种方式检查,我仍然让所有内容都执行stderr/stdout(或任何文件),并使用Python的日志记录功能将消息发送到日志文件(但你真的可以用它做任何事情):

class FileToLogInterface(file):
    '''
    Interface to make sure that everytime anything is written to stderr, it is
    also forwarded to a file.
    '''

    def __init__(self, *args, **kwargs):
        if 'cfg' not in kwargs:
            raise TypeError('argument cfg is required.')
        else:
            if not isinstance(kwargs['cfg'], config.Config):
                raise TypeError(
                    'argument cfg should be a valid '
                    'PostSegmentation configuration object i.e. '
                    'postsegmentation.config.Config')
        self._cfg = kwargs['cfg']
        kwargs.pop('cfg')

        self._logger = logging.getlogger('access_log')

        super(FileToLogInterface, self).__init__(*args, **kwargs)

    def write(self, msg):
        super(FileToLogInterface, self).write(msg)
        self._logger.info(msg)

以下是Yuda Prawira的回答:

实现flush()和所有文件属性 将其编写为上下文管理器 也捕获stderr

.

import contextlib, sys

@contextlib.contextmanager
def log_print(file):
    # capture all outputs to a log file while still printing it
    class Logger:
        def __init__(self, file):
            self.terminal = sys.stdout
            self.log = file

        def write(self, message):
            self.terminal.write(message)
            self.log.write(message)

        def __getattr__(self, attr):
            return getattr(self.terminal, attr)

    logger = Logger(file)

    _stdout = sys.stdout
    _stderr = sys.stderr
    sys.stdout = logger
    sys.stderr = logger
    try:
        yield logger.log
    finally:
        sys.stdout = _stdout
        sys.stderr = _stderr


with log_print(open('mylogfile.log', 'w')):
    print('hello world')
    print('hello world on stderr', file=sys.stderr)

# you can capture the output to a string with:
# with log_print(io.StringIO()) as log:
#   ....
#   print('[captured output]', log.getvalue())

如果您想在Python脚本中进行重定向,请设置sys. exe。对文件对象的Stdout是有用的:

# for python3
import sys
with open('file', 'w') as sys.stdout:
    print('test')

一个更常见的方法是在执行时使用shell重定向(在Windows和Linux上相同):

$ python3 foo.py > file

引用自PEP 343 -“with”语句(添加了import语句):

临时重定向标准输出:

import sys
from contextlib import contextmanager
@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

用途如下:

with open(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"

当然,这不是线程安全的,但手动执行相同的操作也不是。在单线程程序(例如脚本)中,这是一种流行的做事方式。

还有其他使用上下文的版本,但没有这么简单。实际上,我只是在谷歌上仔细检查了一下,并惊讶地没有看到它,所以对于其他人来说,寻找一个快速的解决方案是安全的,只针对上下文块中的代码,这里是:

import sys
with open('test_file', 'w') as sys.stdout:
    print('Testing 1 2 3')

测试如下:

$ cat redirect_stdout.py 导入系统 将open('test_file', 'w')作为sys.stdout: 打印('测试1 2 3') $ python redirect_stdout.py $ cat test_file 测试1 2 3