将stdout重定向到Python中的文件?
如何将stdout重定向到Python中的任意文件 当一个长时间运行的Python脚本(例如,web应用程序)从ssh会话中启动并返回,并且ssh会话关闭时,应用程序将引发IOError,并在尝试写入stdout时失败。我需要找到一种方法,使应用程序和模块输出到一个文件,而不是标准输出,以防止由于IOError导致的故障。目前,我使用nohup将输出重定向到一个文件,这样就完成了工作,但出于好奇,我想知道是否有一种不使用nohup的方法将stdout重定向到Python中的文件?,python,stdout,Python,Stdout,如何将stdout重定向到Python中的任意文件 当一个长时间运行的Python脚本(例如,web应用程序)从ssh会话中启动并返回,并且ssh会话关闭时,应用程序将引发IOError,并在尝试写入stdout时失败。我需要找到一种方法,使应用程序和模块输出到一个文件,而不是标准输出,以防止由于IOError导致的故障。目前,我使用nohup将输出重定向到一个文件,这样就完成了工作,但出于好奇,我想知道是否有一种不使用nohup的方法 我已经尝试了sys.stdout=open('somefi
我已经尝试了
sys.stdout=open('somefile','w')
,但这似乎并不能阻止某些外部模块仍然输出到终端(或者可能是sys.stdout=…
行根本没有触发)。我知道它应该可以在我测试过的更简单的脚本上运行,但我也没有时间在web应用程序上进行测试。如果您想在Python脚本中执行重定向,将sys.stdout
设置为file对象就可以做到这一点:
import sys
sys.stdout = open('stdout.txt', 'w')
import sys
sys.stdout = open('file', 'w')
print('test')
sys.stdout.close()
一种更常见的方法是在执行时使用shell重定向(在Windows和Linux上相同):
你可以试着做得更好
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
其他答案没有涵盖您希望分叉进程共享新标准输出的情况 为此:
from os import open, close, dup, O_WRONLY
old = dup(1)
close(1)
open("file", O_WRONLY) # should open on 1
..... do stuff and then restore
close(1)
dup(old) # should dup to 1
close(old) # get rid of left overs
引自(新增进口声明):
暂时重定向标准输出:
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"
当然,这不是线程安全的,但也不是手动进行同样的舞蹈。在单线程程序中(例如在脚本中),这是一种流行的处理方式
用其他语言编写的程序(如C)必须执行特殊的魔法(称为双分叉),以明确地与终端分离(并防止僵尸进程)。因此,我认为最好的解决办法是模仿他们 重新执行程序的另一个好处是,您可以在命令行上选择重定向,例如
/usr/bin/python mycolscript.py 2>&11>/dev/null
有关更多信息,请参阅本文:Python 3.4+中有:
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 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\u-redirected()
代替redirect\u-stdout()
,则同样的示例现在也适用:
只要stdout\u redirected()
上下文管理器处于活动状态,以前在stdout上打印的输出现在将转到output.txt
注意:stdout.flush()
不刷新
Python3上的C stdio缓冲区,其中I/O直接在read()
/write()
系统调用上实现。要刷新所有打开的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.stderr
和sys.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')
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\u redirected()
混合了缓冲I/O(sys.stdout
通常)和非缓冲I/O(直接对文件描述符的操作)。小心,可能会有
要回答这个问题,您的编辑:您可以使用daemonize对脚本进行后台监控,并使用日志记录模块(as),而不是打印语句,并且只为您现在使用nohup
运行的长时间运行的Python脚本重定向stdout,这是我在我的一个项目中使用的另一种方法。无论用什么替换sys.stderr
或sys.stdout
,都必须确保替换符合文件
接口,特别是如果这是因为stderr/stdout在不受您控制的其他库中使用而要执行的操作。该库可能正在使用文件对象的其他方法
以这种方式检查,我仍然让所有的事情都去做stderr/stdout(或任何相关文件),并使用Python的日志功能将消息发送到日志文件(但您确实可以用它做任何事情):
您需要一个终端多路复用器,如或
我感到惊讶的是,Ryan Amos对原始问题的一个小评论只是提到了一个比所有其他方案都更可取的解决方案,不管python的诡计有多聪明,也不管他们获得了多少赞成票。除了Ryan的评论,tmux是GNU屏幕的一个不错的替代品
但原理是一样的:如果你在注销时发现自己想让终端工作继续运行,那么去咖啡馆吃三明治,去卫生间,回家(等等),然后再从任何地方或任何计算机重新连接到终端会话,就好像你从未离开过一样,终端多路复用器就是答案。可以将它们视为终端会话的VNC或远程桌面。其他任何事情都是一个解决办法。作为奖励,当老板和/或合作伙伴进来时,您无意中用ctrl-w/cmd-w键控制终端窗口而不是浏览器窗口中的不可靠内容时,您不会损失过去18小时的处理时间 这里是Yuda Prawira答案的变体:
- 实现
flush()
和所有文件属性
- 以contextmanager的形式编写
- 捕获
stderr
也
这不是python的东西,它是一个shell函数。只需像script.p>文件那样运行你的脚本
我目前使用nohup解决了这个问题,但我认为可能会有更聪明的东西…@foxbunny:nohup?为什么要这么简单
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)
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)
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())