如何通过subprocess和Popen返回长时间运行进程的stdout?

7 投票
1 回答
6011 浏览
提问于 2025-04-17 21:22

我正在使用一个比较简单的设置,利用subprocess.Popen()来执行命令,并把输出结果保存到一个变量中,之后我会把这个变量返回到我Python脚本的其他部分。

这是我基本的Popen代码:

process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# wait for the process to terminate
out, err = process.communicate()
errcode = process.returncode
print out

这个方法对于很多基本的用例,比如ls -al这样的命令,效果很好。不过,我在想,如何才能从一个运行时间比较长(或者说无限期运行)的进程中,像tail -f foo.log那样,定期并且稳定地获取输出呢?有没有什么办法可以在一个循环中定期读取输出?或者可以启动一个线程,定期检查并返回输出?在这种情况下,最好的方法是什么呢?

谢谢!

1 个回答

9

我觉得有必要指出,原来的代码其实是不正确的(或者说是不安全的)。虽然它通常能工作,但在你给出的例子中,没有任何东西在等这个进程结束。它可能还在运行。

process.poll()process.wait() 是处理这个问题的两个不错的选择。


当你不知道输出会有多大时,使用 communicate 是危险的,因为它会把输出存到内存里,这样可能会导致内存不够用。不过,如果你使用 subprocess.PIPE,这可能已经在发生了。

你应该根据自己的需求仔细选择 stdoutstderr 的目标。如果输出可能非常大,把它写到磁盘上的文件里可能是最好的选择。不过,这个话题可以单独再讨论。


如果你想查看输出而不等进程关闭,可以在一个单独的线程中运行类似这样的代码:

while process.returncode is None:
    # handle output by direct access to stdout and stderr
    for line in process.stdout:
        print line
    # set returncode if the process has exited
    process.poll()

我欢迎大家对如何访问文件对象 stdoutstderr 提出意见,但这是我随便想到的。

虽然这是处理子进程的最稳妥的方法,但如果可以的话,认真考虑使用 process.wait(),因为这样会让一切变得简单很多。

撰写回答