'yes'与子进程通信报告错误()

hyp*_*not 4 python linux shell command-line subprocess

我使用以下函数在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
Run Code Online (Sandbox Code Playgroud)

它一直工作正常,但现在我正在尝试使用该yes程序将输出管道输出到stdin.我正在尝试运行的命令如下:

yes '' | apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
Run Code Online (Sandbox Code Playgroud)

但我相信它可以用一般的例子代替,例如:

yes | head -3 | cat
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果我尝试运行其中的任何命令yes |,上面的subprocess.Popen将包含错误消息:

yes: standard output: Broken pipe
yes: write error
Run Code Online (Sandbox Code Playgroud)

对我来说,似乎管道仍然可以工作,从yes | head -3 | cat答案中可以看出:y y y.

我有以下问题:

  1. 是的管道是否仍然可用,即使是报告错误?
  2. 我该如何解决?

jfs*_*jfs 7

问题是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'
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

yes写错了EPIPE.忽略错误是安全的.它传达相同的信息SIGPIPE.

要解决此问题,您可以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'
Run Code Online (Sandbox Code Playgroud)