之前的答案错过了一个重要的点。正如geocar所指出的,更换shell管道基本上是正确的。在管道的最后一个元素上运行几乎就足够了。communicate
剩下的问题是将输入数据传递到管道。对于多个子流程,communicate(input_data)
最后一个元素上的简单操作不起作用 - 它永远挂起。您需要像这样手动创建一个管道和一个孩子:
import os
import subprocess
input = """\
input data
more input
""" * 10
rd, wr = os.pipe()
if os.fork() != 0: # parent
os.close(wr)
else: # child
os.close(rd)
os.write(wr, input)
os.close(wr)
exit()
p_awk = subprocess.Popen(["awk", "{ print $2; }"],
stdin=rd,
stdout=subprocess.PIPE)
p_sort = subprocess.Popen(["sort"],
stdin=p_awk.stdout,
stdout=subprocess.PIPE)
p_awk.stdout.close()
out, err = p_sort.communicate()
print (out.rstrip())
现在子进程通过管道提供输入,父进程调用communication(),按预期工作。使用这种方法,您可以创建任意长的管道,而无需求助于“将部分工作委托给 shell”。不幸的是,子流程文档没有提到这一点。
有一些方法可以在没有管道的情况下达到相同的效果:
from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)
现在stdin=tf
使用p_awk
. 这是你喜欢什么口味的问题。
以上仍然不是 100% 等效于 bash 管道,因为信号处理不同。如果您添加另一个截断输出的管道元素sort
,例如,您可以看到这一点head -n 10
。使用上面的代码,sort
将打印“Broken pipe”错误消息到stderr
. 当您在 shell 中运行相同的管道时,您不会看到此消息。(这是唯一的区别,结果stdout
是一样的)。原因似乎是 python 的Popen
设置SIG_IGN
为SIGPIPE
,而 shell 将其设置为SIG_DFL
, 和sort
的信号处理在这两种情况下是不同的。