Python 如何使用subprocess.Popen通过管道连接多个进程?
如何使用Python模块执行以下shell命令 输入数据将来自字符串,因此我实际上不需要Python 如何使用subprocess.Popen通过管道连接多个进程?,python,pipe,subprocess,Python,Pipe,Subprocess,如何使用Python模块执行以下shell命令 输入数据将来自字符串,因此我实际上不需要echo。我已经做了这么多了,有人能解释一下我是如何让它也通过排序的吗 p_awk = subprocess.Popen(["awk","-f","script.awk"], stdin=subprocess.PIPE, stdout=file("outfile.txt", "w")) p_awk.comm
echo
。我已经做了这么多了,有人能解释一下我是如何让它也通过排序的吗
p_awk = subprocess.Popen(["awk","-f","script.awk"],
stdin=subprocess.PIPE,
stdout=file("outfile.txt", "w"))
p_awk.communicate( "input data" )
更新:请注意,虽然下面被接受的答案实际上并没有回答所问的问题,但我相信S.洛特是对的,最好不要首先解决这个问题 很好地涵盖了这一点。你有没有不明白的地方
您的程序可能非常类似,但是第二个Popen
将stdout=设置为一个文件,并且您不需要它的.communicate()
的输出,您会对以下内容感到高兴
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。让它用管道连接两个进程
将“script.awk”重写为Python,消除awk和管道,您会更高兴
编辑。一些原因表明awk没有帮助
[有太多理由通过评论作出回应。]
Awk正在添加一个没有重大价值的步骤。awk的处理没有Python无法处理的独特之处
对于大型数据集,从awk到sort的流水线可能会缩短处理时间。对于较短的数据集,它没有显著的好处。awk>文件的快速测量;排序文件
和awk | sort
将显示并发性的帮助。对于sort,它很少有帮助,因为sort不是一个一次性过滤器
“Python-to-sort”处理(而不是“Python-to-awk-to-sort”)的简单性阻止了这里提出的确切问题
Python——虽然比awk更冗长——也是显式的,因为awk有一些对新手来说不透明的隐式规则,而对非专家来说则很混乱
Awk(与shell脚本本身一样)添加了另一种编程语言。如果所有这些都可以用一种语言(Python)完成,那么消除shell和awk编程就可以消除两种编程语言,从而使人们能够专注于任务中产生价值的部分
底线:awk不能增加显著的价值。在这种情况下,awk是一种净成本;它增加了足够的复杂性,因此有必要提出这个问题。删除awk将是一个净收益
侧栏为什么构建管道(a | b
)如此困难
当外壳遇到a | b
时,它必须执行以下操作
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" )
Fork是原始shell的子进程。这最终将成为b
构建一个操作系统管道。(不是Python子进程.PIPE),而是调用os.PIPE()
,它返回两个通过公共缓冲区连接的新文件描述符。在这一点上,进程有来自其父进程的stdin、stdout、stderr,加上一个文件,该文件将是“a的stdout”和“b的stdin”
叉一个孩子。孩子用新的a的stdout替换其stdout。执行a
过程
b子级关闭用新b的stdin替换其stdin。执行b
过程
b子级等待a完成
父级正在等待b完成
我认为上面的代码可以递归地生成a | b | c
,但是您必须隐式地将长管道括起来,将它们视为a |(b | c)
由于Python有os.pipe()
、os.exec()
和os.fork()
,您可以替换sys.stdin
和sys.stdout
,因此纯Python中有一种方法可以实现上述功能。实际上,您可以使用os.pipe()
和subprocess.Popen
来计算一些快捷方式
但是,将该操作委托给shell更容易。要模拟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)
from subprocess import check_call
check_call('echo "input data" | a | b > outfile.txt', shell=True)
不调用shell(请参阅):
提供了一些语法:
#!/usr/bin/env python
from plumbum.cmd import a, b # magic
(a << "input data" | b > "outfile.txt")()
是:
#/usr/bin/env python
从plumbum.cmd导入awk,排序
(awk[“-f”,“script.awk”]“outfile.txt”)()
编辑:管道
在Windows上可用,但至关重要的是,在Windows上似乎无法实际工作。见下面的评论
Python标准库现在包括用于处理此问题的管道
模块:
,
我不确定这个模块已经存在多久了,但这种方法似乎比受@Cristian的答案启发而使用子流程要简单得多。我遇到了同样的问题,但命令不同。因此,我举了一个经过测试的例子,我相信这可能会有所帮助:
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()
这是经过测试的
做了些什么
- 使用管道中的stdin声明惰性
grep
执行。当管道充满ps
标准液时,将在执行ps
命令时执行此命令
- 调用主命令
ps
,stdout指向grep
命令使用的管道
- 格雷普通过电话联系从管道里取下标准液
我喜欢这种方式,因为它是用子流程
接口轻轻包裹起来的自然管道概念。前面的答案忽略了一个要点。正如geocar所指出的,这基本上是正确的。在管道的最后一个元素上运行通信
几乎就足够了
剩下的问题是将输入数据传递到管道。对于多个子流程,最后一个元素上的一个简单的通信(输入数据)
不起作用-它永远挂起。您需要手动创建管道和子管道,如下所示:
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())
现在,子级通过管道提供输入,父级调用communicate(),这将按预期工作。用这个
#!/usr/bin/env python
from plumbum.cmd import awk, sort
(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
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()
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())
from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)
#!/usr/bin/env python3
from subprocess import Popen, PIPE
# cmd1 : dd if=/dev/zero bs=1m count=100
# cmd2 : gzip
# cmd3 : wc -c
cmd1 = ['dd', 'if=/dev/zero', 'bs=1M', 'count=100']
cmd2 = ['tee']
cmd3 = ['wc', '-c']
print(f"Shell style : {' '.join(cmd1)} | {' '.join(cmd2)} | {' '.join(cmd3)}")
p1 = Popen(cmd1, stdout=PIPE, stderr=PIPE) # stderr=PIPE optional, dd is chatty
p2 = Popen(cmd2, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd3, stdin=p2.stdout, stdout=PIPE)
print("Output from last process : " + (p3.communicate()[0]).decode())
# thoretically p1 and p2 may still be running, this ensures we are collecting their return codes
p1.wait()
p2.wait()
print("p1 return: ", p1.returncode)
print("p2 return: ", p2.returncode)
print("p3 return: ", p3.returncode)
from subprocess import Popen, PIPE
def string_to_2_procs_to_file(input_s, first_cmd, second_cmd, output_filename):
with open(output_filename, 'wb') as out_f:
p2 = Popen(second_cmd, stdin=PIPE, stdout=out_f)
p1 = Popen(first_cmd, stdout=p2.stdin, stdin=PIPE)
p1.communicate(input=bytes(input_s))
p1.wait()
p2.stdin.close()
p2.wait()
string_to_2_procs_to_file('input data', ['awk', '-f', 'script.awk'], ['sort'], 'output.txt')