一尘不染

子进程communication()的“是”报告错误

linux

我正在使用以下函数在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

我有以下问题:

  1. 即使yes报告错误,yes管道是否仍然有效?
  2. 我该如何解决?

阅读 259

收藏
2020-06-02

共1个答案

一尘不染

问题在于,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'

yes退出SIGPIPE时被杀死head

在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写错误。可以忽略该错误

要解决该问题,您可以restore_signals使用preexec_fn参数在Python 2中进行仿真:

>>> 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'
2020-06-02