考虑如下代码:
#!/usr/bin/env python p1 = Popen(["cat", "somebigfile"], stdout = PIPE) p2 = Popen(["head"], stdin = p1.stdout, stdout = PIPE) print p2.communicate()[0]
会出现
cat: write error: Broken pipe
原因是在Unix-like系统中,Python启动就会SIG_IGN掉SIG_PIPE,致使cat获取不了SIG_PIPE信号,还向已关闭的管道写入。很久之前就有人提出这个BUG了,这个BUG在Python 3.x已经修复,但是一直没有在Python 2.x实现。
解决方法如下:
1. 下载对应版本的Python源码,找到Lib\subprocess.py(我发现2.6.x的跟2.7.x的就不同)
2. 把该文件放到项目文件夹,改名为sub.py
3. 作如下改动:
--- /usr/lib/python2.6/subprocess.py 2010-04-16 21:58:41.000000000 +0800 +++ sub.py 2011-08-26 17:45:57.576284992 +0800 @@ -429,7 +429,7 @@ import fcntl import pickle -__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] +__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError", "_eintr_retry_call"] try: MAXFD = os.sysconf("SC_OPEN_MAX")
4. 新建new_subprocess.py, 建立Popen_new类,继承原来subprocess的Popen,并把sub.py里面Popen类的POSIX版本的_execute_child方法copy过来。
5. 在
if cwd is not None: os.chdir(cwd)
后面加上如下代码:
import signal signals = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ') for sig in signals: if hasattr(signal, sig): signal.signal(getattr(signal, sig), signal.SIG_DFL)
(此处有一个示例,Python 2.6.5版本的)
6. 在项目里
from new_subprocess import Popen_new as Popen
即可