Python子进程:如何三次使用管道? [复制]

Posted

技术标签:

【中文标题】Python子进程:如何三次使用管道? [复制]【英文标题】:Python subprocess: how to use pipes thrice? [duplicate] 【发布时间】:2012-03-28 04:33:17 【问题描述】:

我想在下面一行使用子进程:

convert ../loxie-orig.png bmp:- | mkbitmap -f 2 -s 2 -t 0.48 | potrace -t 5 --progress -s -o ../DSC00232.svg

感谢其他帖子 the subprocess documentation 但在示例中我们只使用了两次管道。

所以,我尝试了三个命令中的两个,它可以工作

p1 = subprocess.Popen(['convert', fileIn, 'bmp:-'], stdout=subprocess.PIPE)
# p2 = subprocess.Popen(['mkbitmap', '-f', '2', '-s', '2', '-t', '0.48'], stdout=subprocess.PIPE)
p3 = subprocess.Popen(['potrace', '-t' , '5', '-s' , '-o', fileOut], stdin=p1.stdout,stdout=subprocess.PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p3 exits.
output = p3.communicate()[0]

你能帮我完成第三个命令吗?

非常感谢。

【问题讨论】:

您是否再次尝试使用相同进程的第三个命令? 为什么您尝试过的方法不起作用?管道在 shell 中所做的所有事情就是将一个进程的标准输出连接到另一个进程的标准输入。 我不知道我必须在哪里关闭 p2 以及如何对输出进行编码...... @Zorkzyd 从示例中可以清楚地看出进程如何相互挂钩, P1.STDOUT -> P2.STDIN : P2.STDOUT -> P3.STDIN : P3 具有您想要的输出。您可能只需要仔细阅读 对不起,我是新手。我试图自己理解,但这并不总是那么容易……但是,非常感谢您的回答。 【参考方案1】:

只需在相同的示例之后添加第三个命令:

p1 = subprocess.Popen(['convert', fileIn, 'bmp:-'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['mkbitmap', '-f', '2', '-s', '2', '-t', '0.48'], 
     stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
p3 = subprocess.Popen(['potrace', '-t' , '5', '-s' , '-o', fileOut],        
     stdin=p2.stdout,stdout=subprocess.PIPE)
p2.stdout.close()

output = p3.communicate()[0]

【讨论】:

我不这么认为,既然它们都是链接的,我认为应该只需要一个communicate 调用。成功了吗? 是的,现在可以使用了。谢谢你:) p1.stdout.close()p2.stdout.close() 跟在 output = p3.communicate()[0] 之后有关系吗? 你为什么不用p1.wait(),就在p1.stdout.close()p2.wait()之前,就在p2.stdout.close()之前;这个答案(***.com/a/13332300/2402577)使用了哪个(ps.wait())? @HunterMcMillen 值得注意的是,stdin 必须是有效的 IO 对象。尝试使用 urllib3(通过请求)没有运气 - 存在死锁。在我看来,通常子流程在不必要的低级别运行。我正在使用请求,但必须通过子进程更改“curl”以使其工作而不会出现死锁。【参考方案2】:

subprocess.Popen()shell=True 选项一起使用,您可以将整个命令作为单个字符串传递。

这是最简单的解决方案,可以在 python 中嵌入复杂的管道而不会让人头疼;但在某些情况下它可能不起作用,例如(正如@torek 评论的那样)如果传递给输入或输出的文件名中有空格。在这种情况下,请在accepted answer. 中构建强大的解决方案

【讨论】:

这通常是要走的路(它肯定要容易得多),但有时您可能会遇到文件名引用问题,例如,在这里,如果 fileInfileOut 在名字。大多数时候可以使用pipes.quote 来处理这些问题,但离开shell=False 在技术上更安全。【参考方案3】:
def runPipe(cmds):
try: 
    p1 = subprocess.Popen(cmds[0].split(' '), stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    prev = p1
    for cmd in cmds[1:]:
        p = subprocess.Popen(cmd.split(' '), stdin = prev.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
        prev = p
    stdout, stderr = p.communicate()
    p.wait()
    returncode = p.returncode
except Exception, e:
    stderr = str(e)
    returncode = -1
if returncode == 0:
    return (True, stdout.strip().split('\n'))
else:
    return (False, stderr)

然后像这样执行:

runPipe(['ls -1','head -n 2', 'head -n 1'])

【讨论】:

我想知道是否有人使用 subprocess.Popen 的替换?我正在使用 git 在 repo 上运行相同的命令,它工作正常,而当我尝试使用 subprocess.Popen 和 python 3.6 运行相同的命令并调用 byy github 操作时,它的抛出错误如致命:模棱两可的 Head

以上是关于Python子进程:如何三次使用管道? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

带有管道的子进程调用[重复]

两个子进程之间的 Python 管道输出

带有管道和大文件的 Python 子进程

使用子进程时如何在 Python 中复制 tee 行为?

使用子进程时如何在 Python 中复制 tee 行为?

Python 子进程中的 ffmpeg - 无法为“管道:”找到合适的输出格式