使用python从进程捕获输出的最佳方法是什么?

问题描述 投票:5回答:1

我正在使用python的subprocess模块来启动新进程。我想实时捕获新过程的输出,以便我可以对其进行处理(显示,解析等)。我已经看到了许多有关如何完成此操作的示例,有些使用自定义的类文件对象,有些使用threading,有些尝试在过程完成之前读取输出。

File Like Objects Example (click me)

  • 我不想使用自定义的类文件对象,因为我想允许用户为stdinstdoutstderr提供自己的值。

Threading Example (click me)

  • 我真的不明白为什么需要线程,所以我不愿意遵循这个例子。如果有人可以解释为什么该线程示例有意义,那么我很乐意听。但是,此示例还限制用户提供自己的stdoutstderr值。

读取输出示例(请参见下文)

对我来说最有意义的示例是读取stdoutstderr,直到过程完成。这是一些示例代码:

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捕获进程的输出?

python subprocess stdout
1个回答
0
投票

首先,您的设计有点傻,因为您可以执行以下相同操作:

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)。

© www.soinside.com 2019 - 2024. All rights reserved.