捕获子进程输出
捕获子进程输出
我了解到在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()后不会继续执行,并等待命令完成。有没有其他方法来跟踪输出呢?
问题:为什么要使用.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
来实时获取子进程的输出。
问题原因: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的回答。