将stdoutredirect到Python中的文件?
如何将stdoutredirect到Python中的任意文件?
当一个长时间运行的Python脚本(例如,web应用程序)从ssh会话中启动,并且ssh会话closures时,应用程序将会提高IOError,并在尝试写入标准输出时失败。 我需要find一种方法,使应用程序和模块输出到一个文件而不是标准输出,以防止由于IOError失败。 目前,我使用nohup来将输出redirect到一个文件,并且完成了这项工作,但是我想知道是否有办法做到这一点,而不使用nohup,出于好奇。
我已经尝试了sys.stdout = open('somefile', 'w')
,但是这似乎并没有阻止一些外部模块输出到terminal(或者sys.stdout = ...
所有)。 我知道它应该从我testing过的简单脚本开始工作,但是我还没有时间去testing一个web应用程序。
如果你想在Python脚本中进行redirect,可以将sys.stdout
设置为一个文件对象:
import sys sys.stdout = open('file', 'w') print 'test'
更常见的方法是在执行时使用shellredirect(在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版本上使用。 后一版本不可重复使用 。 如果需要,可以制成一个。
它不会在文件描述符级别redirect标准输出,例如:
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'
没有redirect到output.txt
文件。
要在文件描述符级别redirect,可以使用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')
只要stdout_redirected()
上下文pipe理器处于活动状态,以前在stdout上输出的输出就会转到output.txt
。
注意:在直接在read()
/ write()
系统调用上实现I / O的Python 3上, stdout.flush()
不会刷新C stdio缓冲区。 要刷新所有打开的C stdio输出stream,如果某个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
参数来redirect其他stream,不仅是sys.stdout
比如合并sys.stderr
和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
模块(如@ erikb85build议 )而不是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
其他答案并没有涵盖你要分叉进程共享你的新的标准输出的情况。
要做到这一点:
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
从PEP引用343 – “with”语句 (添加import语句):
暂时redirect标准输出:
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 opened(filename, "w") as f: with stdout_redirected(f): print "Hello world"
当然,这不是线程安全的,但是也不是手动做同样的舞蹈。 在单线程程序中(例如在脚本中),这是一种stream行的做事方式。
import sys sys.stdout = open('stdout.txt', 'w')
您需要像tmux或GNU屏幕一样的terminal多路复用器
令我感到惊讶的是,Ryan Amos对原始问题的一个小小的评论是唯一提到的解决scheme比提供的所有其他解决scheme更可取,不pipe它的诡计多么聪明,他们收到了多lessupvotes。 除了Ryan的评论之外,tmux是GNU屏幕的一个不错的select。
但是原理是一样的:如果你发现自己想要在登出的时候离开一个terminal工作,就去咖啡馆吃三明治,去卫生间,回家(等等),然后重新连接到你的terminal会话从任何地方或任何电脑,就像你永远不会离开,terminal多路复用器是答案。 把它们想象成VNC或远程桌面来进行terminal会话。 其他任何事情都是解决方法。 作为奖励,当老板和/或合伙人进来时,你无意中按你的terminal窗口,而不是你的浏览器窗口与其不友好的内容,你不会失去了最后18小时的处理!
基于这个答案: https : //stackoverflow.com/a/5916874/1060344 ,这是另一种方法,我想出了我在我的项目之一使用。 无论你用什么replacesys.stderr
或者sys.stdout
,你都必须确保replace符合file
接口,特别是如果这是你正在做的事情,因为stderr / stdout在其他一些不受控制的库中使用。 该库可能正在使用其他文件对象的方法。
检查出这种方式,我仍然让所有的东西做stderr /标准输出(或任何文件的事情),并发送消息到日志文件使用Python的日志logging工具(但你真的可以做任何事情):
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 ie ' '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)
用其他语言(例如C)编写的程序必须明确地执行特殊的魔法(称为双重分离)以从terminal分离(并且防止僵尸进程)。 所以,我认为最好的解决办法就是效仿它们。
重新执行你的程序的一个/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
是,你可以select命令行上的redirect,例如/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
看到这个post的更多信息: 创build一个守护进程时执行一个双叉的原因是什么?