将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.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模块(如@ 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一个守护进程时执行一个双叉的原因是什么?