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

如何使用Python subprocess模块执行以下shell命令?

 echo "input data" | awk -f script.awk | sort > outfile.txt 

input数据将来自一个string,所以我实际上不需要echo 。 我有这么远,任何人都可以解释我怎么得到它通过sort吗?

 p_awk = subprocess.Popen(["awk","-f","script.awk"], stdin=subprocess.PIPE, stdout=file("outfile.txt", "w")) p_awk.communicate( "input data" ) 

更新 :请注意,虽然下面接受的答案实际上并没有回答问题,但我相信S.Lott是正确的,最好是避免首先解决这个问题!

以下是你会更快乐一点。

 import subprocess awk_sort = subprocess.Popen( "awk -f script.awk | sort > outfile.txt", stdin=subprocess.PIPE, shell=True ) awk_sort.communicate( b"input data\n" ) 

将部分工作委托给shell。 让它用stream水线连接两个进程。

如果将'script.awk'改写成Python,你会更加快乐,从而消除awk和pipe道。

编辑 。 提出awk的一些原因没有帮助。

[通过评论回复的原因太多了。]

  1. Awk正在增加一个没有重要价值的步骤。 awk的处理没有什么特别的,Python不能处理。

  2. 从awkstream水线sorting,对于大量数据,可能会缩短处理时间。 对于短的数据集,它没有显着的好处。 快速测量awk >file ; sort file awk >file ; sort fileawk | sort awk | sort会显示并发的帮助。 sorting,它很less帮助,因为sorting不是一次filter。

  3. “Pythonsorting”处理的简单性(而不是“Python来sorting”)可以防止在这里提出的确切types的问题。

  4. Python – 而不是awk–比awk更加单调 – 也是明确的,awk有一些隐含的规则,对新手来说是不透明的,而对于非专业人员来说却是混乱的。

  5. awk(如shell脚本本身)增加了另一种编程语言。 如果所有这些都可以使用一种语言(Python)来完成,那么消除shell和awk编程就可以消除两种编程语言,从而使得人们可以专注于任务的产生价值的部分。

底线:awk不能增加显着的价值。 在这种情况下,awk是一个净成本; 它增加了足够的复杂性,有必要问这个问题。 删除awk将是一个净收益。

边栏为什么build立pipe道( a | b )太难了。

当shell遇到a | b a | b必须做到以下几点。

  1. 分叉原壳的子过程。 这将最终成为b。

  2. build立一个ospipe道。 (不是Python子os.pipe() ,而是调用os.pipe() ,它返回两个通过公共缓冲区连接的新文件描述符。 在这一点上,这个进程有父,标准输出,标准错误,加上一个文件,将是“A的标准输出”和“B的标准input”。

  3. 叉一个孩子。 孩子用新的标准输出replace它的标准输出。 执行a过程。

  4. b孩子closures用新的b的stdinreplace它的stdin。 执行b过程。

  5. b孩子等待完成。

  6. 父母正在等待b完成。

我认为上面可以recursion地用来产生a | b | c a | b | c a | b | c ,但是你必须隐式地加长长pipe道,把它们看作是a | (b | c) a | (b | c)

由于Python有os.pipe()os.exec()os.fork() ,你可以replacesys.stdinsys.stdout ,有一种方法可以在纯Python中完成上述操作。 事实上,你可以使用os.pipe()os.pipe()来创build一些快捷方式。

但是,将该操作委托给shell更容易。

 import subprocess some_string = b'input_data' sort_out = open('outfile.txt', 'wb', 0) sort_in = subprocess.Popen('sort', stdin=subprocess.PIPE, stdout=sort_out).stdin subprocess.Popen(['awk', '-f', 'script.awk'], stdout=sort_in, stdin=subprocess.PIPE).communicate(some_string) 

要模拟一个shellpipe道:

 from subprocess import check_call check_call('echo "input data" | a | b > outfile.txt', shell=True) 

而无需调用shell(参见17.1.4.2。replaceshellpipe道 ):

 #!/usr/bin/env python from subprocess import Popen, PIPE a = Popen(["a"], stdin=PIPE, stdout=PIPE) with a.stdin: with a.stdout, open("outfile.txt", "wb") as outfile: b = Popen(["b"], stdin=a.stdout, stdout=outfile) a.stdin.write(b"input data") statuses = [a.wait(), b.wait()] # both a.stdin/stdout are closed already 

plumbum提供了一些语法糖:

 #!/usr/bin/env python from plumbum.cmd import a, b # magic (a << "input data" | b > "outfile.txt")() 

类比:

 #!/bin/sh echo "input data" | awk -f script.awk | sort > outfile.txt 

是:

 #!/usr/bin/env python from plumbum.cmd import awk, sort (awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")() 

http://www.python.org/doc/2.5.2/lib/node535.html涵盖了这个很好。; 有没有这个你不明白的部分?

你的程序是非常相似的,但第二个Popen会有stdout =到一个文件,你不需要它的.communicate()的输出。

受@ Cristian的回答启发。 我遇到了同样的问题,但是使用了不同的命令。 所以我把我的testing的例子,我相信可能会有所帮助:

 grep_proc = subprocess.Popen(["grep", "rabbitmq"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) subprocess.Popen(["ps", "aux"], stdout=grep_proc.stdin) out, err = grep_proc.communicate() 

这是testing。

已经做了什么

  • 用pipe从stdin声明了懒惰的grep执行。 这个命令将在执行ps命令时执行,当pipe道将被填充ps的标准输出。
  • 调用主命令ps ,stdout指向grep命令使用的pipe道。
  • grep沟通,从pipe道获得标准输出。

我喜欢这种方式,因为它是用subprocess接口轻轻包裹的自然pipe道概念。

编辑: pipes是在Windows上可用,但关键是,似乎并没有实际上在Windows上工作 。 见下面的评论。

Python标准库现在包括用于处理这个的pipes模块:

https://docs.python.org/2/library/pipes.html,https://docs.python.org/3.4/library/pipes.html

我不确定这个模块已经存在了多长时间,但是这种方法似乎比用于subprocess进程的方法简单得多。