我正在使用python的subprocess
模块来启动新进程。我想实时捕获新过程的输出,以便我可以对其进行处理(显示,解析等)。我已经看到了许多有关如何完成此操作的示例,有些使用自定义的类文件对象,有些使用threading
,有些尝试在过程完成之前读取输出。
File Like Objects Example (click me)
stdin
,stdout
和stderr
提供自己的值。stdout
和stderr
值。读取输出示例(请参见下文)
对我来说最有意义的示例是读取stdout
,stderr
,直到过程完成。这是一些示例代码:
import subprocess
# Start a process which prints the options to the python program.
process = subprocess.Popen(
["python", "-h"],
bufsize=1,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# While the process is running, display the output to the user.
while True:
# Read standard output data.
for stdout_line in iter(process.stdout.readline, ""):
# Display standard output data.
sys.stdout.write(stdout_line)
# Read standard error data.
for stderr_line in iter(process.stderr.readline, ""):
# Display standard error data.
sys.stderr.write(stderr_line)
# If the process is complete - exit loop.
if process.poll() != None:
break
我的问题是,
Q。是否有推荐的方法使用python捕获进程的输出?
首先,您的设计有点傻,因为您可以执行以下相同操作:
process = subprocess.Popen(
["python", "-h"],
bufsize=1,
stdout=sys.stdout,
stderr=sys.stderr
)
…甚至更好:
process = subprocess.Popen(
["python", "-h"],
bufsize=1
)
但是,我认为这只是一个玩具示例,您可能想做一些更有用的事情。
您设计的主要问题是,直到完成stderr
,它才会从stdout
中读取任何内容。
假设您正在驾驶一个MP3播放器,该播放器将每个曲目名称打印到stdout,并将信息记录到stderr,并且您想播放10首歌曲。您是否真的要等待30分钟,然后再向用户显示任何日志记录?
如果可以接受,那么您不妨使用communicate
,它可以为您解决所有麻烦。另外,即使您的模型可以接受,您确定可以在管道中排队那么多未读数据而不会阻塞子级吗?在每个平台上?
只是打破循环以在两者之间交替将无济于事,因为您可能会在stdout.readline()
堆积时最终阻塞stderr
5分钟。
所以这就是为什么您需要某种方式一次读取两者的原因。
这是与一次处理1000个网络客户端相同的问题(但较小),并且具有相同的解决方案:线程化或多路复用(以及各种混合,例如在多路复用器和事件循环的顶部执行绿色线程,或者使用螺纹转换器等)。
线程版本的最佳示例代码是3.2+源代码中的communicate
。这有点复杂,但是如果您想在Windows和Unix上正确处理所有的极端情况,实际上并没有避免一些复杂性。
[用于多路复用,您可以使用communicate
模块,但是请记住,这仅适用于Unix(Windows上的管道上无法select
),并且如果没有3.2+(或select
backport),并且要正确处理所有边缘情况,您需要在select
中添加信号处理程序。除非您真的真的不想使用线程,否则这是更难的答案。
但是
easy
答案是使用其他人的实现。 PyPI上有十几个或更多模块专门用于异步子流程。另外,如果您已经有充分的理由围绕事件循环编写应用,那么几乎每个现代的事件循环驱动的异步网络库(包括stdlib的subprocess32
)都提供了对子流程的支持,这两种方式都可以使用Unix和Windows。是否有推荐的方法使用python捕获进程的输出?取决于您要问的人;一千个Python开发人员可能会有一千个不同的答案,或者至少是六个。如果您要问核心开发人员会推荐什么,我可以猜测:
如果不需要异步捕获它,请使用select
(但请确保至少升级到3.2,以修复重要的错误)。如果确实需要异步捕获,请使用asyncio
(需要3.4)。