subprocess.Popen 管道如何在 Python 中工作?

Posted

技术标签:

【中文标题】subprocess.Popen 管道如何在 Python 中工作?【英文标题】:How do subprocess.Popen pipes work in Python? 【发布时间】:2015-02-08 02:07:27 【问题描述】:

我完全知道相关问题的存在,但所有这些问题都非常模糊,无法清楚地说明每一步发生了什么。提供的示例通常未经测试,也没有提供有关如何使它们适应不同场景的信息。以下是问题以及 Python 的文档:

How do I use subprocess.Popen to connect multiple processes by pipes? link several Popen commands with pipes https://docs.python.org/2/library/subprocess.html#popen-objects

这本质上是我想要实现的,但是在 Python 中:

curl http://asdf.com/89asdf.gif | convert -resize 80x80 - - | icat -k -

以下是我在下班后将上述答案的部分和部分整理在一起的内容:

import requests
import subprocess
from subprocess import Popen, PIPE, STDOUT
url = 'http://asdf.com/89asdf.gif'
img = requests.get(url)
p1 = subprocess.Popen(['convert', '-resize', '80x80', '-', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p2 = subprocess.Popen(['icat', '-k', '-'], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.communicate(img.content)
print p2.stdout

这是我的代码,这次根据您的回答进行了改进:

import requests
from subprocess import Popen, PIPE, STDOUT
url = 'http://asdf.com/89asdf.gif'
img = requests.get(url)
p1 = Popen(['convert', '-resize', '80x80', '-', '-'], stdin=PIPE, stdout=PIPE)
p2 = Popen(['icat', '-k', '-'], stdin=p1.stdout, stdout=PIPE)
p1.stdin.write(img.content)
p1.stdin.close()
p1.stdout.close()
output = p2.communicate()[0]
print output

注意:icat 在支持 256 色的终端中输出图像。我已经设法在another question 中成功打印了它的输出。 icat 处理后的典型图像在终端中如下所示:

请纠正我的错误,但这是我目前的理解:

p1.communicate(img.content):这设置了 p1 的 STDIN。 p1 的 STDIN 是 subprocess.PIPE,这是 p1.communicate(mg.content) 提供的。 p2的STDIN是p1的STDOUT,也就是convert调整大小的图片。 然后我printp2的STDOUT,应该是icat提供的ASCII颜色的图片。

问题:

    stdin=subprocess.PIPEstdin=PIPE 我都见过。有什么区别? 为什么在定义其他子流程之后才提供第一个 STDIN?

有人能解释一下这个过程中实际发生了什么以及我的代码有什么问题吗?整个管道业务一定比看起来简单,我很想了解它是如何运作的。

【问题讨论】:

抱歉,我只是仔细查看了您的代码 - 我昨天一定是太累了。对我的答案进行了编辑,可能会有所帮助。 【参考方案1】:

第一季度。无论是subprocess.PIPE 还是PIPE,它们都引用相同的符号,即来自subprocess 模块的PIPE。以下是相同的:

# Version 1
import subprocess
proc = subprocess.Popen(['ls'], stdout=subprocess.PIPE)
output = proc.communicate()[0]

# Version 2
from subprocess import PIPE, Popen
proc = Popen(['ls'], stdout=PIPE)
output = proc.communicate()[0]

第二季度。 communicate() 实际上正在做的是将输入发送到 p1 的 STDIN 流。尽管在调用 Popen 构造函数时子进程确实处于活动状态并且正在运行,但在您的特定情况下,convert 实用程序似乎在它实际通过 STDIN 接收内容之前不会做任何事情。如果它是一个交互性较低的命令(例如 ls),那么它不会等到 communicate() 再做任何事情。

更新内容 在您的情况下,不要使用 communicate 将输入发送到 p1,而是尝试以下操作:

p1.stdin.write(img.content)
p1.stdin.close()
p1.stdout.close() # Protect against the busted pipe condition when p2 finishes before p1
output = p2.communicate()[0]
print output

看看你是否有更好的运气。

【讨论】:

subprocess.PIPEPIPE 之间的区别现在非常清楚。谢谢你。我根据您的“版本 2”示例简化了我的代码。【参考方案2】:

第一季度。他们都是subprocess.PIPE ....

第二季度。你不必这样做,你可以很容易地做到这一点

p1.stdin.write(img.content)

(事实上,这正是通信部分所做的......)

communicate 被使用是因为它会阻塞直到进程完成(在这种情况下,它的标准输出可以对 p2.stdin(也许?)可用)其中write 只是写入管道并继续下一个文件中的python行

【讨论】:

以上是关于subprocess.Popen 管道如何在 Python 中工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 subprocess.Popen 通过管道连接多个进程?

如何将 Subprocess.popen() 与 pyinstaller 一起使用?

后台进程 subprocess.Popen 与管道

管道输出subprocess.Popen到文件

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

subprocess.Popen 解析命令列表出现问题;管道未正确发送