将标准输出重定向到 Python 中的文件?

新手上路,请多包涵

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

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

我已经尝试过 sys.stdout = open('somefile', 'w') ,但这似乎并不能阻止某些外部模块仍然输出到终端(或者 sys.stdout = ... 线根本没有触发)。我知道它应该可以从我测试过的更简单的脚本中运行,但我还没有时间在 Web 应用程序上进行测试。

原文由 user234932 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 641
2 个回答

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

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

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

 $ python3 foo.py > file

原文由 moinudin 发布,翻译遵循 CC BY-SA 4.0 许可协议

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 版本上使用。后一个版本不可 重复使用。如果需要,它可以做成一个。

它不会在文件描述符级别重定向标准输出,例如:

 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 now\n')
    os.system('echo this is also redirected')
print('this is goes back to stdout')

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

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

 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

You could use stdout parameter to redirect other streams, not only sys.stdout eg, to merge sys.stderr and sys.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 脚本。

原文由 jfs 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题