【问题标题】:Python subprocess.Popen blocks with shell and pipePython subprocess.Popen 带有外壳和管道的块
【发布时间】:2014-05-28 16:20:51
【问题描述】:

运行以下命令:yes | head -1,从 shell 输出 y。使用python的子进程模块用shell调用它无限期挂起:

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/shGNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)

【问题讨论】:

  • 使用"Command ended: %r" % (r,),因为r是一个元组。
  • 我无法在 Ubuntu 上的 Python 2.7 上重现它。它工作正常 +/- the expected "write" error.
  • 谢谢,那个链接就足够解决了!

标签: python


【解决方案1】:

所以事实证明这是由于一个已知问题导致 python 在subprocess 模块中的 exec 之前没有重置信号处理程序。

这与导致'yes' reporting error with subprocess communicate() 的问题相同,除了对于 GNU yes,写入返回的 EPIPE 会导致程序中止,而对于 BSD yes(就我而言,它在 OS X 上使用可以看出),写的返回码没有检查,所以没有SIGPIPEyes不会终止。

可以通过向后移植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

【讨论】: