捕获子进程输出

19 浏览
0 Comments

捕获子进程输出

我了解到在Python中执行命令时,应该使用subprocess。

我想要实现的是通过ffmpeg对文件进行编码,并观察程序输出,直到文件处理完成。ffmpeg会将进度记录到stderr。

如果我尝试像下面这样的代码:

child = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE)
complete = False
while not complete:
    stderr = child.communicate()
    # 获取进度
    print "稍后显示进度"
    if child.poll() is not None:
        complete = True
    time.sleep(2)

程序在调用child.communicate()后不会继续执行,并等待命令完成。有没有其他方法来跟踪输出呢?

0
0 Comments

问题:为什么要使用.communicate()方法来捕获子进程的输出?有没有其他的解决方法?

当我们需要运行一个子进程,并捕获其输出时,通常会使用.communicate()方法来实现。这个方法会等待子进程终止,并读取其标准输出和标准错误流直到文件末尾。然而,在某些情况下,我们可能只需要读取标准错误流,而不需要等待子进程终止。

Python的subprocess模块提供了一种更简单的方法来解决这个问题。我们可以直接读取child.stderr,就像读取一个普通的文件一样。这种方法不需要等待子进程终止,可以实时获取子进程的输出。

原因:使用.communicate()方法来捕获子进程的输出可能会导致不必要的等待时间,特别是当我们只需要读取标准错误流时。

解决方法:可以直接读取child.stderr来获取子进程的标准错误流输出,而无需使用.communicate()方法。

以下是一个示例代码,演示如何使用child.stderr来实时获取子进程的输出:

import subprocess
def capture_subprocess_output(command):
    child = subprocess.Popen(command, stderr=subprocess.PIPE)
    for line in child.stderr:
        print(line.decode().strip())
capture_subprocess_output(["ls", "-l"])

在上述示例中,我们使用subprocess.PIPE参数来创建一个子进程,并将其标准错误流重定向到child.stderr。然后,我们可以通过遍历child.stderr来实时获取子进程的输出。

0
0 Comments

问题原因:communicate()方法在子进程返回之前会阻塞程序执行,所以循环中的其他代码只会在子进程完成运行后执行。从stderr读取数据也会阻塞,除非按字符读取。

解决方法:按字符逐个读取stderr的数据,以实现实时输出。可以使用subprocess.Popen方法创建子进程,并设置stderr参数为subprocess.PIPE,通过循环读取stderr的一个字符来实现。当读取到的字符为空且子进程已经结束时,跳出循环。如果读取到的字符不为空,则将其写入stdout,并刷新输出缓冲区。

代码如下:

import subprocess
import sys
child = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE)
while True:
    out = child.stderr.read(1)
    if out == '' and child.poll() is not None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

以上代码来自Nadia在stackoverflow上的回答,可以实现实时输出。在这里不需要用到child.poll()方法,可以在循环结束后调用child.wait()方法。作为一个挑剔的人,最好将"!= None"改写为"is not None"。

以上内容整理自Nadia在stackoverflow的回答。

0