Python subprocess.Popen 在使用 shell 和 pipe 时阻塞
运行以下命令:yes | head -1
,在命令行中会输出一个y
。但是,如果用Python的subprocess模块来调用这个命令,程序就会一直卡在那里,不会继续执行:
import subprocess
arg = "yes | head -1"
process = subprocess.Popen(arg,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print "Command started: %d" % process.pid
r = process.communicate()
print "Command ended: %s %s" % r
即使用kill
命令强制结束这个进程也没有用,或者尝试通过preexec_fn=os.setsid
让子进程成为它的会话领导者也不行。
这是什么原因造成的呢?有没有办法能解决这个问题?
我使用的是Python 2.7.3,/bin/sh
是GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
。
1 个回答
1
原来这是因为 Python 在 subprocess
模块中没有在执行之前重置信号处理程序,这个问题是个已知的bug。
这个问题和 'yes' 在 subprocess communicate() 中报错 的情况是一样的。不同的是,GNU 的 yes
在写入时遇到 EPIPE 错误会导致程序直接崩溃,而 BSD 的 yes
(在我看来,OS X 上用的就是这个)在写入时不会检查返回代码,所以如果没有 SIGPIPE
信号,yes
就不会停止。
这个问题可以通过将 reset_signals
的代码移植过来解决,具体方法可以参考上面提到的问题:
def restore_signals():
signals = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ')
for sig in signals:
if hasattr(signal, sig):
signal.signal(getattr(signal, sig), signal.SIG_DFL)
然后在 Popen
调用中设置 preexec_fn=restore_signals
。