【问题标题】:What is the best way to capture output from a process using python?使用 python 从进程中捕获输出的最佳方法是什么?
【发布时间】:2014-02-02 00:11:48
【问题描述】:

我正在使用 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

我的问题是,

问。是否有推荐的方法来使用 python 捕获进程的输出?

【问题讨论】:

  • 可以给个输入输出的例子吗?
  • 哈,就是这样!我正在比较不同版本的 python 的输出。菜鸟失误!!我从原始问题中删除了截断问题。谢谢你让我看起来更努力一点。

标签: python subprocess stdout


【解决方案1】:

首先,你的设计有点傻,因为你可以这样做:

process = subprocess.Popen(
                           ["python", "-h"],
                           bufsize=1,
                           stdout=sys.stdout,
                           stderr=sys.stderr
                           )

……或者,甚至更好:

process = subprocess.Popen(
                           ["python", "-h"],
                           bufsize=1
                           )

但是,我假设这只是一个玩具示例,您可能想做一些更有用的事情。


您的设计的主要问题是在完成stdout 之前,它不会从stderr 读取任何内容。

假设您正在驾驶一个 MP3 播放器,它将每个曲目名称打印到标准输出,并将信息记录到标准错误,并且您想要播放 10 首歌曲。您真的要等待 30 分钟再向您的用户显示任何日志记录吗?

如果可以接受的,那么您不妨使用communicate,它会为您解决所有问题。

另外,即使您的模型可以接受,您确定可以在管道中排队那么多未读数据而不会阻塞子节点吗?在每个平台上?

仅仅打破循环以在两者之间交替不会有帮助,因为您最终可能会在 stderr 堆积时阻塞 stdout.readline() 5 分钟。

这就是为什么您需要某种方式同时读取两者的原因。


如何同时读取两个管道?

这与一次处理 1000 个网络客户端的问题相同(但更小),并且具有相同的解决方案:线程或多路复用(以及各种混合,例如在多路复用器和事件循环之上执行绿色线程,或使用线程前处理器等)。

线程版本的最佳示例代码是来自 3.2+ 源代码的communicate。这有点复杂,但如果你想在 Windows 和 Unix 上正确处理所有的边缘情况,那么确实无法避免一点复杂性。

对于多路复用,您可以使用 select 模块,但请记住,这仅适用于 Unix(您不能在 Windows 上的管道上使用 select),并且如果没有 3.2+(或 @987654335 @ backport),并且要真正获得所有边缘情况,您需要将信号处理程序添加到您的select。除非你真的、真的不想使用线程,否则这是更难的答案。

简单的答案是使用别人的实现。 PyPI 上有十几个或更多模块专门用于异步子进程。或者,如果您已经有充分的理由围绕事件循环编写应用程序,那么几乎每个现代事件循环驱动的异步网络库(包括 stdlib 的 asyncio)都包含开箱即用的子进程支持,这两者都适用Unix 和 Windows。


有没有推荐的方法来使用 python 捕获进程的输出?

这取决于你问的是谁;一千个 Python 开发人员可能有一千个不同的答案……或者至少有六个。如果你问核心开发者会推荐什么,我可以猜测一下:

如果您不需要异步捕获它,请使用communicate(但请确保升级到至少 3.2 以修复重要的错误)。如果确实需要异步捕获,请使用asyncio(需要 3.4)。

【讨论】:

  • 首先,我没有否决这个答案。非常感谢您的解释。其次,我对使用默认 io 值(sys.stdout 和 sys.stderr)犹豫不决,因为某些应用程序倾向于覆盖这些值(在我的情况下,它是一个名为 Maya 的 3D 应用程序。它有自己的 python 解释器并使用某种类型io 流的自定义类文件对象)。提供 subprocess.PIPE 似乎可以确保一些一致性。我将看一下python 3.2+中的示例。谢谢!
  • 我发布了一个新问题,希望能更具体地描述我的问题。请看一下。感谢您的意见。 stackoverflow.com/questions/21247721/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-25
  • 2011-05-09
  • 2019-08-20
  • 1970-01-01
  • 2020-10-10
  • 2012-02-25
  • 1970-01-01
相关资源
最近更新 更多