【问题标题】:'yes' reporting error with subprocess communicate()'是'报告错误与子进程通信()
【发布时间】:2014-03-31 10:26:41
【问题描述】:

我正在使用以下函数在 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 程序将输出通过管道传输到标准输入。我尝试运行的命令如下:

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. 我该如何解决?

【问题讨论】:

标签: python linux shell command-line subprocess


【解决方案1】:

问题在于subprocess 模块before Python 3.2+ doesn't restore SIGPIPE signal handler to default action。这就是为什么您会收到 EPIPE 写入错误。

在 Python 3.2+ 中

>>> from subprocess import check_output
>>> check_output("yes | head -3", shell=True)
b'y\ny\ny\n'

yes 在head 退出时被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 写入错误。忽略该错误是安全的。 It communicates the same information as 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'

【讨论】:

  • 感谢您,我很欣赏这样一个事实,如果您从 shell 脚本移植过来,这不需要更改您的代码。
【解决方案2】:

另一个问题回答了为什么......我会试着给你一个解决方法

你不能做类似的事情

proc = subprocess.Popen(cmd, shell=True, 
                             stdout=subprocess.PIPE, 
                             stderr=subprocess.PIPE,
                             stdin = subprocess.PIPE)

for i in range(10):  #send 10 y's
    time.sleep(1) # 1 second apart
    proc.stdin.write("y") #you may also need to send a newline ...

print proc.communicate()

见下文(我没有为延迟烦恼,因为 head 并没有真正做太多事情)

>>> import subprocess
>>> proc = subprocess.Popen("head -3",
...                          shell = True,
...                          stdout = subprocess.PIPE,
...                          stderr=subprocess.PIPE,
...                          stdin=subprocess.PIPE)
>>> for i in range(10):
...    proc.stdin.write("y\n")
...
>>> proc.communicate()
('y\ny\ny\n', '')

【讨论】:

  • 这很酷,但是对于比“是”更复杂的命令,它可能需要重写 Python 中的所有功能,这可能是也可能不是首选解决方案。
【解决方案3】:

说:

yes | head -3

导致head 在读取 3 行输入后向yes 发送一个SIGPIPE,即它会发送一个终止yes 的信号。

$ yes | head -3
y
y
y
$ echo "${PIPESTATUS[@]}"
141 0

解决方案是避免SIGPIPE!

【讨论】:

猜你喜欢
  • 2023-01-05
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2014-02-14
  • 1970-01-01
  • 2015-05-19
  • 1970-01-01
相关资源
最近更新 更多