Python 的 Popen 清理
我想在Python中实现类似于在Perl中使用管道命令的功能。就像用Python写的open(PIPE, "command |")那样。
我查阅了subprocess模块,尝试了以下代码:
p = subprocess.Popen("zgrep thingiwant largefile", shell=True, stdout=subprocess.PIPE)
这个方法可以像在Perl中一样读取输出,但它没有自动清理。当我退出解释器时,我看到
grep: writing output: Broken pipe
在错误输出中出现了几百万次的错误信息。我原本天真地以为这些问题会自动处理好,但事实并非如此。调用terminate或kill来结束进程p似乎也没有帮助。查看进程列表,我发现这确实结束了/bin/sh进程,但留下了子进程gzip在那儿抱怨管道断开了。
那我该怎么做才对呢?
4 个回答
你是怎么执行这个过程的?
正确的方法是使用
p.communicate()
想了解更多细节,可以查看文档。
在你打开管道之后,就可以使用命令的输出了:p.stdout
:
for line in p.stdout:
# do stuff
p.stdout.close()
问题在于,pipe
已经满了。子进程停止了,等着这个管道里的数据被处理掉,但你的程序(Python 解释器)却退出了,这样就断开了管道的一端(所以才会出现错误信息)。
p.wait()
并不能解决这个问题:
警告 如果子进程输出的数据太多,导致管道满了,程序就会卡住,等着操作系统的管道缓冲区能接受更多数据。为了避免这种情况,使用
communicate()
。http://docs.python.org/library/subprocess.html#subprocess.Popen.wait
p.communicate()
也不能解决这个问题:
注意 读取的数据会在内存中缓存,所以如果数据量很大或者没有限制,就不要使用这个方法。
http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate
p.stdout.read(num_bytes)
也不能解决这个问题:
警告 使用
communicate()
而不是.stdin.write
、.stdout.read
或.stderr.read
,以避免由于其他操作系统管道缓冲区满而导致的死锁。http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout
这个故事的教训是,对于大量输出,subprocess.PIPE
会让你的程序注定失败,如果你的程序试图读取数据(我觉得你可以把 p.stdout.read(bytes)
放在 while p.returncode is None:
的循环里,但上面的警告表明这可能会导致死锁)。
文档建议用这个来替代管道:
p1 = Popen(["zgrep", "thingiwant", "largefile"], stdout=PIPE)
p2 = Popen(["processreceivingdata"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
注意,p2
是直接从 p1
获取标准输入的。这 应该 能避免死锁,但考虑到上面的矛盾警告,谁知道呢。
无论如何,如果最后那部分对你不起作用(不过 应该 有用),你可以尝试创建一个临时文件,把第一次调用的所有数据写入这个文件,然后用这个临时文件作为下一个进程的输入。