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

时间:2014-02-27 18:59:18

标签: 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

它一直运行良好,但是现在我尝试使用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. 是的管道是否仍然有效,即使是报告错误?
  2. 我该如何解决?

3 个答案:

答案 0 :(得分:7)

问题在于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退出时,<SIGPIPEhead杀死。

在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

要解决此问题,您可以使用restore_signals参数在Python 2中模拟preexec_fn

>>> 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'

答案 1 :(得分:3)

另一个问题回答了原因......我会试着给你一个解决方法

你能不能做像

这样的事情
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()

见下文(由于头部没有做太多事情,我没有理会延迟)

>>> 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', '')

答案 2 :(得分:2)

话说:

yes | head -3

导致head在读完3行输入后向SIGPIPE发送yes,即它会发送信号以终止yes

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

解决方案是避免使用SIGPIPE