使用子进程时如何在 Python 中复制 tee 行为?
我正在寻找一个 Python 解决方案,它允许我将命令的输出保存在文件中,而不将其从控制台隐藏。
仅供参考:我正在询问 tee (作为 Unix 命令行实用程序)和不是 Python intertools 模块中的同名函数。
详细信息
- Python 解决方案(不调用
tee
,它在 Windows 下不可用) - 我不需要为被调用进程向 stdin 提供任何输入
- 我无法控制被调用程序。我所知道的是它会向 stdout 和 stderr 输出一些内容并返回退出代码。
- 在调用外部程序(子进程)时工作
- 对
stderr
和stdout
都 - 适用 能够区分 stdout 和 stderr,因为我可能只想显示其中之一控制台或者我可以尝试使用不同的颜色输出 stderr - 这意味着 stderr = subprocess.STDOUT 将不起作用。
- 实时输出(渐进式)- 该过程可以运行很长时间,而且我无法等待它完成。
- Python 3 兼容代码(重要)
参考资料
以下是我迄今为止发现的一些不完整的解决方案:
- http://devlishgenius.blogspot.com/2008/10/logging-in-real-time-in-python.html(mkfifo 仅适用于 Unix)
- http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen- output.html (根本不起作用)
图 http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
当前代码(第二次尝试)
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
Real outputstderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
预期输出是对行进行排序。请注意,修改 Popen 以仅使用一个 PIPE 是不允许的,因为在现实生活中我会想要使用 stderr 和 stdout 做不同的事情。
此外,即使在第二种情况下,我也无法获得实时的结果,事实上,当过程完成时,所有结果都已收到。默认情况下,Popen 不应使用任何缓冲区 (bufsize=0)。
I'm looking for a Python solution that will allow me to save the output of a command in a file without hiding it from the console.
FYI: I'm asking about tee (as the Unix command line utility) and not the function with the same name from Python intertools module.
Details
- Python solution (not calling
tee
, it is not available under Windows) - I do not need to provide any input to stdin for called process
- I have no control over the called program. All I know is that it will output something to stdout and stderr and return with an exit code.
- To work when calling external programs (subprocess)
- To work for both
stderr
andstdout
- Being able to differentiate between stdout and stderr because I may want to display only one of the to the console or I could try to output stderr using a different color - this means that
stderr = subprocess.STDOUT
will not work. - Live output (progressive) - the process can run for a long time, and I'm not able to wait for it to finish.
- Python 3 compatible code (important)
References
Here are some incomplete solutions I found so far:
- http://devlishgenius.blogspot.com/2008/10/logging-in-real-time-in-python.html (mkfifo works only on Unix)
- http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html (doesn't work at all)
Diagram http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
Current code (second try)
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
Real output
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
Expected output was to have the lines ordered. Remark, modifying the Popen to use only one PIPE is not allowed because in the real life I will want to do different things with stderr and stdout.
Also even in the second case I was not able to obtain real-time like out, in fact all the results were received when the process finished. By default, Popen should use no buffers (bufsize=0).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
如果需要 python 3.6 不是问题,现在可以使用
asyncio
来实现此目的。此方法允许您分别捕获 stdout 和 stderr,但仍然将两个流传输到 tty,而不使用线程。这是一个粗略的概述:上面的代码基于此博客文章:https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
If requiring python 3.6 isn't an issue there is now a way of doing this using
asyncio
. This method allows you to capture stdout and stderr separately but still have both stream to the tty without using threads. Here's a rough outline:The code above was based on this blog post: https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
我发现这是一篇相当旧的帖子,但以防万一有人仍在寻找一种方法来做到这一点:
I see that this is a rather old post but just in case someone is still searching for a way to do this:
这是
tee(1)
的简单移植Python。我现在在 Linux 上运行,但这应该适用于大多数平台。
现在对于
subprocess
部分,我不知道如何“连接”子进程的stdin
、stdout
和stderr< /code> 到您的
stdin
、stdout
、stderr
和文件接收器,但我知道您可以这样做:现在您可以访问
callee.stdin
,callee.stdout
和callee.stderr
像普通文件一样,启用上述功能“解决方案”发挥作用。如果您想获取callee.returncode
,您需要额外调用 <代码>callee.poll()。写入
callee.stdin
时要小心:如果执行此操作时进程已退出,可能会出现错误(在 Linux 上,我收到IOError: [Errno 32] Broken pipeline
代码>)。This is a straightforward port of
tee(1)
to Python.I'm running on Linux right now but this ought to work on most platforms.
Now for the
subprocess
part, I don't know how you want to 'wire' the subprocess'sstdin
,stdout
andstderr
to yourstdin
,stdout
,stderr
and file sinks, but I know you can do this:Now you can access
callee.stdin
,callee.stdout
andcallee.stderr
like normal files, enabling the above "solution" to work. If you want to get thecallee.returncode
, you'll need to make an extra call tocallee.poll()
.Be careful with writing to
callee.stdin
: if the process has exited when you do that, an error may be rised (on Linux, I getIOError: [Errno 32] Broken pipe
).这是可以做到的
This is how it can be done
根据社区 wiki 的回答,这里有一个更新版本。
gather
而不是wait
(wait
给出警告)str
这是一个完整的文件,您可以运行;超时设置为 5 秒,因此应该超时。
注意:Python 默认缓冲 stdout,因此您需要在任何地方使用
-u
。Based on the community wiki answer, here is an updated version.
gather
instead ofwait
(wait
gives a warning)str
This is a complete file that you can run; the timeout is set to 5 seconds so it should time out.
NOTE: Python buffers stdout by default so you need to use
-u
everywhere.从使用
tee
的简单示例开始(稍后我将向您展示在没有tee
的情况下也可以执行此操作),您可以执行以下操作:此处:
stderr
和stdout
。t_out
,运行tee
,并且我们仅捕获stderr
(允许stdout从tee
流出) > 通常)t_err
执行相同的操作,但从p
发送stderr
并仅捕获stdout
(允许stderr
正常流向 stderr)最终结果是命令的 stdout 和 stderr 正常输出到终端,并在返回的子进程中捕获。
假设有一个写入 stderr 和 stdout 的简单程序:
您可以这样做:
除了程序的输出之外,您还可以看到 Python 脚本可以读取 stdout 和 stderr:
不使用
tee
请注意,使用程序
tee
实际上并不是必需的。这可以很容易地成为一个纯 python 程序,它读取 stdin 并像tee
一样读取输出。例如,可以改用以下 Python 脚本。它所做的只是读取 stdin 并将其打印到 stdout 和 stderr。
然后第一个示例可以这样修改:
最终结果与第一个示例类似,但不需要程序
tee
。该解决方案也不一定需要使用额外的子流程。这只是一种方法。可以在使用第一个子进程的 stderr/stdout 的两个线程中完成相同的解决方案。
注意:如果 stdout 和 stderr 大约同时写入,则需要进行一些重大更改才能保证消息到达终端的正确顺序。
Starting with a simple example using
tee
(I'll show you can do this withouttee
later) you can do the following:Here:
p
, capturing bothstderr
andstdout
.t_out
, runningtee
, and we capture onlystderr
(allowing stdout to flow out fromtee
normally)t_err
, but sendingstderr
fromp
and capturing onlystdout
(allowingstderr
to flow normally to stderr)The end result is that stdout and stderr of your command are output to the terminal normally and also captured in the returned subprocesses.
Suppose a simple program that writes to stderr and stdout:
You could do:
In addition to the program's output, you can see that stdout and stderr are readable by the Python script:
Without using
tee
Note that use of the program
tee
is not actually necessary. This could just as easily be a pure python program that reads stdin and tees the output the same astee
does.For example, the following Python script can be used instead. All it does is read stdin and print it to stdout and stderr.
Then the first example can be modified like this:
The end result is similar to the first example, but doesn't require the program
tee
.This solution also doesn't necessarily require the use of the additional subprocesses. It's just one way to do it. The same solution could be done in two threads that consume stderr/stdout of the first subprocess.
CAVEAT: some substantial changes would be needed to guarantee correct order of arrival of messages to the terminal if stdout and stderr are written to at about the same time.
如果您不想与进程交互,可以使用 subprocess 模块。
示例:
tester.py
test.py
在您的情况下,您可以先将 stdout/stderr 写入文件。您也可以通过通信向进程发送参数,尽管我无法弄清楚如何持续与子进程交互。
If you don't want to interact with the process you can use the subprocess module just fine.
Example:
tester.py
testing.py
In your situation you can simply write stdout/stderr to a file first. You can send arguments to your process with communicate as well, though I wasn't able to figure out how to continually interact with the subprocess.
在 Linux 上,如果您确实需要类似
tee(2 )
系统调用,你可以这样得到它:要使用它,你可能想要使用 Python 3.10 和带有
os.splice
的东西(或者使用ctypes< /code> 以同样的方式获得
splice
)。请参阅tee(2)
手册页< /a> 为例。On Linux, if you really need something like the
tee(2)
syscall, you can get it like this:To use this, you probably want to use Python 3.10 and something with
os.splice
(or usectypes
in the same way to getsplice
). See thetee(2)
man page for an example.我的解决方案并不优雅,但它有效。
您可以使用 powershell 在 WinOS 下访问“tee”。
My solution isn't elegant, but it works.
You can use powershell to gain access to "tee" under WinOS.