将 stdout 重定向到 Python 中的文件?
如何将 stdout 重定向到 Python 中的任意文件?
当长时间运行的 Python 脚本(例如 Web 应用程序)从 ssh 会话中启动并处于后台,并且 ssh 会话关闭时,应用程序将引发 IOError 并在尝试写入 stdout 时失败。我需要找到一种方法使应用程序和模块输出到文件而不是标准输出,以防止由于 IOError 导致失败。目前,我使用 nohup 将输出重定向到文件,这样就完成了工作,但出于好奇,我想知道是否有一种方法可以在不使用 nohup 的情况下完成此任务。
我已经尝试过 sys.stdout = open('somefile', 'w') ,但这似乎并不能阻止某些外部模块仍然输出到终端(或者可能是 sys.stdout )。 stdout = ... 行根本没有触发)。我知道它应该可以从我测试过的更简单的脚本中运行,但我还没有时间在 Web 应用程序上进行测试。
How do I redirect stdout to an arbitrary file in Python?
When a long-running Python script (e.g, web application) is started from within the ssh session and backgounded, and the ssh session is closed, the application will raise IOError and fail the moment it tries to write to stdout. I needed to find a way to make the application and modules output to a file rather than stdout to prevent failure due to IOError. Currently, I employ nohup to redirect output to a file, and that gets the job done, but I was wondering if there was a way to do it without using nohup, out of curiosity.
I have already tried sys.stdout = open('somefile', 'w')
, but this does not seem to prevent some external modules from still outputting to terminal (or maybe the sys.stdout = ...
line did not fire at all). I know it should work from simpler scripts I've tested on, but I also didn't have time yet to test on a web application yet.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
如果您想在 Python 脚本中进行重定向,将 sys.stdout 设置为文件对象即可:
更常见的方法是在执行时使用 shell 重定向(在 Windows 和 Linux 上也是如此) :
If you want to do the redirection within the Python script, setting
sys.stdout
to a file object does the trick:A far more common method is to use shell redirection when executing (same on Windows and Linux):
有
contextlib.redirect_stdout()
函数 在 Python 3.4+ 中:它类似于:
可以在早期的 Python 版本上使用。后一个版本不可可重用。如果需要的话可以制作一个。
它不会在文件描述符级别重定向标准输出,例如:
b'not redirected'
和'echo this is not redirected'
不会重定向到输出.txt
文件。要在文件描述符级别重定向,可以使用 os.dup2() :
如果使用 stdout_redirected() 而不是 redirect_stdout(),则现在可以使用相同的示例:
只要
stdout_redirected()
上下文管理器处于活动状态,以前在 stdout 上打印的输出现在就会转到output.txt
。注意:
stdout.flush()
不会刷新Python 3 上的 C stdio 缓冲区,其中 I/O 直接在
read()
/write()
系统调用上实现。要刷新所有打开的 C stdio 输出流,如果某些 C 扩展使用基于 stdio 的 I/O,则可以显式调用libc.fflush(None)
:您可以使用
stdout
参数重定向其他流,而不仅仅是sys.stdout
例如,合并sys.stderr
和sys.stdout
:示例:
注意:
stdout_redirected()
混合缓冲 I/O(通常为 sys.stdout)和非缓冲 I/O(直接对文件描述符进行操作)。请注意,可能存在缓冲问题。要回答,您的编辑:您可以使用
python-daemon
守护程序化您的脚本并使用logging
模块(如 @erikb85 建议)而不是print
语句,并且仅重定向您现在使用nohup
运行的长时间运行的 Python 脚本的标准输出。There is
contextlib.redirect_stdout()
function in Python 3.4+:It is similar to:
that can be used on earlier Python versions. The latter version is not reusable. It can be made one if desired.
It doesn't redirect the stdout at the file descriptors level e.g.:
b'not redirected'
and'echo this also is not redirected'
are not redirected to theoutput.txt
file.To redirect at the file descriptor level,
os.dup2()
could be used:The same example works now if
stdout_redirected()
is used instead ofredirect_stdout()
:The output that previously was printed on stdout now goes to
output.txt
as long asstdout_redirected()
context manager is active.Note:
stdout.flush()
does not flushC stdio buffers on Python 3 where I/O is implemented directly on
read()
/write()
system calls. To flush all open C stdio output streams, you could calllibc.fflush(None)
explicitly if some C extension uses stdio-based I/O:You could use
stdout
parameter to redirect other streams, not onlysys.stdout
e.g., to mergesys.stderr
andsys.stdout
:Example:
Note:
stdout_redirected()
mixes buffered I/O (sys.stdout
usually) and unbuffered I/O (operations on file descriptors directly). Beware, there could be buffering issues.To answer, your edit: you could use
python-daemon
to daemonize your script and uselogging
module (as @erikb85 suggested) instead ofprint
statements and merely redirecting stdout for your long-running Python script that you run usingnohup
now.你可以尝试这个更好
you can try this too much better
其他答案没有涵盖您希望分叉进程共享新标准输出的情况。
为此:
The other answers didn't cover the case where you want forked processes to share your new stdout.
To do that:
引自 PEP 343 -- The "with" 语句 (添加了 import 语句) :
暂时重定向标准输出:
用法如下:
当然,这不是线程安全的,但手动执行相同的操作也不是。在单线程程序中(例如在脚本中),这是一种流行的处理方式。
Quoted from PEP 343 -- The "with" Statement (added import statement):
Redirect stdout temporarily:
Used as follows:
This isn't thread-safe, of course, but neither is doing this same dance manually. In single-threaded programs (for example in scripts) it is a popular way of doing things.
这是 Yuda Prarawa 答案的一个变体:
flush()
并且所有文件属性stderr
。
Here is a variation of Yuda Prawira answer:
flush()
and all the file attributesstderr
also.
您需要一个终端多路复用器,例如 tmux 或 GNU screen
我很惊讶 Ryan Amos 对原始问题的一个小评论是唯一提到的解决方案,远优于所有其他提供的解决方案,无论蟒蛇的诡计多么聪明,无论他们收到了多少支持。根据 Ryan 的评论,tmux 是 GNU screen 的一个很好的替代品。
但原理是相同的:如果您发现自己想在注销时让终端作业继续运行,请前往咖啡馆吃个三明治,去洗手间,回家(等等),然后重新连接到您的计算机。从任何地方或任何计算机进行终端会话就好像您从未离开过一样,终端多路复用器就是答案。将它们视为终端会话的 VNC 或远程桌面。其他任何方法都是解决方法。作为奖励,当老板和/或合作伙伴进来并且您无意中 ctrl-w / cmd-w 终端窗口而不是浏览器窗口及其可疑内容时,您不会丢失过去 18 小时的处理时间!
You need a terminal multiplexer like either tmux or GNU screen
I'm surprised that a small comment by Ryan Amos' to the original question is the only mention of a solution far preferable to all the others on offer, no matter how clever the python trickery may be and how many upvotes they've received. Further to Ryan's comment, tmux is a nice alternative to GNU screen.
But the principle is the same: if you ever find yourself wanting to leave a terminal job running while you log-out, head to the cafe for a sandwich, pop to the bathroom, go home (etc) and then later, reconnect to your terminal session from anywhere or any computer as though you'd never been away, terminal multiplexers are the answer. Think of them as VNC or remote desktop for terminal sessions. Anything else is a workaround. As a bonus, when the boss and/or partner comes in and you inadvertently ctrl-w / cmd-w your terminal window instead of your browser window with its dodgy content, you won't have lost the last 18 hours-worth of processing!
基于这个答案:https://stackoverflow.com/a/5916874/1060344,这是我想出的另一种方法我在我的一个项目中使用它。无论您用什么替换
sys.stderr
或sys.stdout
,您都必须确保替换符合file
接口,特别是如果是您正在做的事情,因为 stderr/stdout 在不受您控制的其他一些库中使用。该库可能正在使用文件对象的其他方法。看看这种方式,我仍然让一切都去做 stderr/stdout (或任何与此相关的文件),并使用 Python 的日志记录工具将消息发送到日志文件(但你真的可以用它做任何事情):
Based on this answer: https://stackoverflow.com/a/5916874/1060344, here is another way I figured out which I use in one of my projects. For whatever you replace
sys.stderr
orsys.stdout
with, you have to make sure that the replacement complies withfile
interface, especially if this is something you are doing because stderr/stdout are used in some other library that is not under your control. That library may be using other methods of file object.Check out this way where I still let everything go do stderr/stdout (or any file for that matter) and also send the message to a log file using Python's logging facility (but you can really do anything with this):
用其他语言(例如 C)编写的程序必须明确地执行特殊的魔法(称为双分叉)以从终端分离(并防止僵尸进程)。所以,我认为最好的解决办法就是效仿他们。
重新执行程序的一个优点是,您可以在命令行上选择重定向,例如
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
请参阅此发布更多信息:什么创建守护进程时执行双分叉的原因是什么?
Programs written in other languages (e.g. C) have to do special magic (called double-forking) expressly to detach from the terminal (and to prevent zombie processes). So, I think the best solution is to emulate them.
A plus of re-executing your program is, you can choose redirections on the command-line, e.g.
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
See this post for more info: What is the reason for performing a double fork when creating a daemon?
我知道这个问题已经得到解答(使用
python abc.py > output.log 2>&1
),但我仍然不得不说:编写程序时,不要写入标准输出。始终使用日志记录来输出您想要的任何内容。当您将来想要重定向、过滤、旋转输出文件时,这将为您提供很大的自由。
I know this question is answered (using
python abc.py > output.log 2>&1
), but I still have to say:When writing your program, don't write to stdout. Always use logging to output whatever you want. That would give you a lot of freedom in the future when you want to redirect, filter, rotate the output files.
正如 @jfs 所提到的,大多数解决方案都无法正确处理某些类型的 stdout 输出,例如来自 C 扩展的输出。 PyPI 上有一个名为
wurlitzer
的模块负责处理所有这些事情。您只需要它的 sys_pipes 上下文管理器。就像使用一样简单:As mentioned by @jfs, most solutions will not properly handle some types of stdout output such as that from C extensions. There is a module that takes care of all this on PyPI called
wurlitzer
. You just need itssys_pipes
context manager. It's as easy as using:根据这篇文章之前的答案,我为自己编写了这个类,作为一种更紧凑、更灵活的方式来重定向代码段的输出(这里只是到一个列表),并确保之后输出标准化。
用作:
更新。刚刚发现一个场景,我必须添加两个额外的方法,但很容易适应:
Based on previous answers on this post I wrote this class for myself as a more compact and flexible way of redirecting the output of pieces of code - here just to a list - and ensure that the output is normalized afterwards.
Used as:
Updating. Just found a scenario where I had to add two extra methods, but was easy to adapt:
还有其他使用上下文的版本,但没有这么简单。实际上,我只是在谷歌上仔细检查它是否有效,但很惊讶没有看到它,因此对于其他正在寻找安全且仅针对上下文块中的代码的快速解决方案的人来说,这里是:
测试如下:
There are other versions using context but nothing this simple. I actually just googled to double check it would work and was surprised not to see it, so for other people looking for a quick solution that is safe and directed at only the code within the context block, here it is:
Tested like so:
对于那些感兴趣的人,我扩展了这个问题。我需要写入日志文件一段时间,然后关闭它,重命名它并随后使用正常的标准输出。我该怎么做?
在stdout上会有:
在xxx上会有:
如果程序异常退出,xxx文件将不存在。
For those interested, I extend the question. I need to write to log file for a while, then close it, rename it and use normal stdout afterwards. How do I do it?
On stdout there will be:
In xxx there will be:
If the program exits abnormally, the xxx file won't exist.