带有标准输入的 subprocess.Popen.communicate() 的管道损坏

Posted

技术标签:

【中文标题】带有标准输入的 subprocess.Popen.communicate() 的管道损坏【英文标题】:Broken Pipe from subprocess.Popen.communciate() with stdin 【发布时间】:2011-01-05 06:54:13 【问题描述】:

我在使用 subprocess.Popen.communicate() 时遇到了一个奇怪的问题。作为背景,我想从我的 python 脚本中执行一个应用程序。当我从命令行运行程序时,我会这样做(UNIX):

$ echo "输入文本" | /path/to/myapp

从我的脚本中,我还想将输入通过管道传输到应用程序中。所以,我尝试了以下方法。但是当我尝试使用communicate() 发送输入时出现“管道损坏”错误:

>>> cmd = ['/path/to/myapp'] >>> p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,stdin=subprocess.PIPE) >>> out,err = p.communicate('输入文本') 回溯(最近一次通话最后): 文件“”,第 1 行,在 文件“/usr/lib/python2.5/subprocess.py”,第 670 行,在通信中 返回self._communicate(输入) _communicate 中的文件“/usr/lib/python2.5/subprocess.py”,第 1223 行 bytes_written = self._write_no_intr(self.stdin.fileno(), buffer(input, input_offset, 512)) _write_no_intr 中的文件“/usr/lib/python2.5/subprocess.py”,第 1003 行 返回 os.write(fd, s) OSError:[Errno 32] 损坏的管道

为了让事情变得更奇怪,如果我遗漏输入数据,我不会收到任何错误。但是,这并不是一个很好的解决方法,因为应用程序需要输入才能工作。

>>> p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,stdin=subprocess.PIPE) >>> out,err = p.communicate() >>> 打印出来 [myapp 关于缺少输入的错误]

知道我错过了什么吗?

【问题讨论】:

【参考方案1】:

您的观察表明 myapp 在没有读取(所有)输入的情况下终止。对myapp一无所知,这很难确认,但请考虑一下

$ echo 'hello world' | tr 'l' 'L'
heLLo worLd

现在...:

>>> cmd = ['/usr/bin/tr']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
>>> out,err = p.communicate('hello world')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/subprocess.py", line 668, in communicate
    return self._communicate(input)
  File "/usr/lib/python2.5/subprocess.py", line 1218, in _communicate
    bytes_written = self._write_no_intr(self.stdin.fileno(), buffer(input, input_offset, 512))
  File "/usr/lib/python2.5/subprocess.py", line 997, in _write_no_intr
    return os.write(fd, s)
OSError: [Errno 32] Broken pipe

因为...:

>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
>>> /usr/bin/tr: missing operand
Try `/usr/bin/tr --help' for more information.

如果我们修复错误:

>>> cmd = ['/usr/bin/tr', 'l', 'L']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
>>> out,err = p.communicate('hello world')>>> print out
heLLo worLd
>>> print err
None

...它解决了所有问题。如果您省略 stderr 重定向会发生什么——您是否会看到来自 myapp... 的任何错误消息?

【讨论】:

我可以在 2.6 上重现这个,但在 2.7 上没有足够大的输入(例如communicate('foo\n'*100000))。是否更改了通信()对 EPIPE 的处理?

以上是关于带有标准输入的 subprocess.Popen.communicate() 的管道损坏的主要内容,如果未能解决你的问题,请参考以下文章

subprocess.Popen 和缓冲的进程输出

如何从 subprocess.Popen 使用 STDIN [重复]

python中的subprocess.Popen()使用

如何在 Windows 上使用带有内置命令的 subprocess.Popen

为啥带有 shell=True 的 subprocess.Popen() 在 Linux 和 Windows 上的工作方式不同?

如何在 Python 2 中将字符串传递给 subprocess.Popen? [复制]