使用subprocess communicate()时报告'yes'错误
我在用下面这个函数在Python里运行命令:
def run_proc(cmd):
child = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = child.communicate()
returncode = child.returncode
return stdout, stderr, returncode
这个函数一直都能正常工作,不过现在我想用yes
这个程序来把输出传给标准输入(stdin)。我想运行的命令是:
yes '' | apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
但我觉得可以用一个更通用的例子来替代,比如:
yes | head -3 | cat
我的问题是,如果我尝试运行任何包含yes |
的命令,上面的subprocess.Popen就会出现错误信息:
yes: standard output: Broken pipe
yes: write error
对我来说,似乎这个管道还是能正常工作的,因为从yes | head -3 | cat
的结果来看,输出是y y y
。
我有以下几个问题:
- 即使
yes
报告了错误,管道功能还是有效的吗? - 我该怎么解决这个问题?
3 个回答
9
这个问题是因为在Python 3.2之前,subprocess
模块没有把SIGPIPE
信号的处理方式恢复到默认状态。这就是为什么你会看到EPIPE
写入错误的原因。
在Python 3.2及之后的版本中:
>>> from subprocess import check_output
>>> check_output("yes | head -3", shell=True)
b'y\ny\ny\n'
当head
退出时,yes
会被SIGPIPE
信号杀掉。
在Python 2中:
>>> from subprocess import check_output
>>> check_output("yes | head -3", shell=True)
yes: standard output: Broken pipe
yes: write error
'y\ny\ny\n'
yes
会出现EPIPE
写入错误。这个错误可以安全地忽略。它传达的信息和SIGPIPE
是一样的。
为了绕过这个问题,你可以在Python 2中使用preexec_fn
参数来模拟restore_signals
:
>>> from subprocess import check_output
>>> import signal
>>> def restore_signals(): # from http://hg.python.org/cpython/rev/768722b2ae0a/
... signals = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ')
... for sig in signals:
... if hasattr(signal, sig):
... signal.signal(getattr(signal, sig), signal.SIG_DFL)
...
>>> check_output("yes | head -3", shell=True, preexec_fn=restore_signals)
'y\ny\ny\n'