将stdout重定向到Python中的文件?

如何将stdout重定向到Python中的任意文件?

当一个长时间运行的Python脚本(例如web应用程序)从ssh会话中启动并且背景变暗,并且ssh会话关闭时,应用程序将提高IOError并在尝试写入标准输出时失败。 我需要找到一种方法将应用程序和模块输出到文件而不是标准输出,以防止由于IOError而导致失败。 目前,我使用nohup将输出重定向到一个文件,并完成了工作,但我想知道是否有办法做到这一点,而不使用nohup,出于好奇。

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


如果你想在Python脚本中进行重定向,请将sys.stdout设置为文件对象。

import sys
sys.stdout = open('file', 'w')
print 'test'

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

$ python foo.py > file

Python 3.4中有contextlib.redirect_stdout()函数:

from contextlib import redirect_stdout

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        print('it now prints to `help.text`')

它类似于:

import sys
from contextlib import contextmanager

@contextmanager
def redirect_stdout(new_target):
    old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
    try:
        yield new_target # run some code with the replaced stdout
    finally:
        sys.stdout = old_target # restore to the previous value

可以在早期的Python版本上使用。 后一版本不可重复使用。 如果需要,可以制成一个。

它不会在文件描述符级别重定向stdout,例如:

import os
from contextlib import redirect_stdout

stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, redirect_stdout(f):
    print('redirected to a file')
    os.write(stdout_fd, b'not redirected')
    os.system('echo this also is not redirected')

b'not redirected''echo this also is not redirected'没有被重定向到output.txt文件。

要在文件描述符级别重定向,可以使用os.dup2()

import os
import sys
from contextlib import contextmanager

def fileno(file_or_fd):
    fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
    if not isinstance(fd, int):
        raise ValueError("Expected a file (`.fileno()`) or a file descriptor")
    return fd

@contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
    if stdout is None:
       stdout = sys.stdout

    stdout_fd = fileno(stdout)
    # copy stdout_fd before it is overwritten
    #NOTE: `copied` is inheritable on Windows when duplicating a standard stream
    with os.fdopen(os.dup(stdout_fd), 'wb') as copied: 
        stdout.flush()  # flush library buffers that dup2 knows nothing about
        try:
            os.dup2(fileno(to), stdout_fd)  # $ exec >&to
        except ValueError:  # filename
            with open(to, 'wb') as to_file:
                os.dup2(to_file.fileno(), stdout_fd)  # $ exec > to
        try:
            yield stdout # allow code to be run with the redirected stdout
        finally:
            # restore stdout to its previous value
            #NOTE: dup2 makes stdout_fd inheritable unconditionally
            stdout.flush()
            os.dup2(copied.fileno(), stdout_fd)  # $ exec >&copied

现在,如果使用stdout_redirected()而不是redirect_stdout()现在可以使用相同的示例:

import os
import sys

stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, stdout_redirected(f):
    print('redirected to a file')
    os.write(stdout_fd, b'it is redirected nown')
    os.system('echo this is also redirected')
print('this is goes back to stdout')

只要stdout_redirected()上下文管理器处于活动状态,以前在stdout上打印的输出现在会转到output.txt

注意:在直接在read() / write()系统调用上实现I / O的Python 3上, stdout.flush()不会刷新C stdio缓冲区。 要清除所有打开的C stdio输出流,如果某些C扩展使用基于stdio的I / O,则可以明确调用libc.fflush(None)

try:
    import ctypes
    from ctypes.util import find_library
except ImportError:
    libc = None
else:
    try:
        libc = ctypes.cdll.msvcrt # Windows
    except OSError:
        libc = ctypes.cdll.LoadLibrary(find_library('c'))

def flush(stream):
    try:
        libc.fflush(None)
        stream.flush()
    except (AttributeError, ValueError, IOError):
        pass # unsupported

你可以使用stdout参数重定向其他流,而不仅仅是sys.stdout例如,合并sys.stderrsys.stdout

def merged_stderr_stdout():  # $ exec 2>&1
    return stdout_redirected(to=sys.stdout, stdout=sys.stderr)

例:

from __future__ import print_function
import sys

with merged_stderr_stdout():
     print('this is printed on stdout')
     print('this is also printed on stdout', file=sys.stderr)

注意: stdout_redirected()混合缓冲的I / O(通常是sys.stdout )和非缓冲的I / O(直接对文件描述符进行操作)。 当心,可能会有缓冲问题。

要回答你的编辑问题:你可以使用python-daemon来守护你的脚本,并使用logging模块(如@ erikb85建议)而不是print语句,而只是重新定向你使用nohup运行的长时间运行的Python脚本的stdout。


你可以尝试这个更好

import sys

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

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

sys.stdout = Logger("yourlogfilename.txt")
print "Hello world !" # this is should be saved in yourlogfilename.txt
链接地址: http://www.djcxy.com/p/42313.html

上一篇: Redirect stdout to a file in Python?

下一篇: atomic writing to file with Python