使用subprocess.Popen时将大量数据管道传输到stdin

2024-04-26 07:58:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我有点难以理解python是如何解决这个简单问题的。

我的问题很简单。如果你使用以下代码,它将挂起。这在子流程模块文档中有很好的记录。

import subprocess

proc = subprocess.Popen(['cat','-'],
                        stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE,
                        )
for i in range(100000):
    proc.stdin.write('%d\n' % i)
output = proc.communicate()[0]
print output

寻找一个解决方案(有一个很有见地的线索,但我现在已经失去了它)我发现这个解决方案(除其他外)使用了一个显式分叉:

import os
import sys
from subprocess import Popen, PIPE

def produce(to_sed):
    for i in range(100000):
        to_sed.write("%d\n" % i)
        to_sed.flush()
    #this would happen implicitly, anyway, but is here for the example
    to_sed.close()

def consume(from_sed):
    while 1:
        res = from_sed.readline()
        if not res:
            sys.exit(0)
            #sys.exit(proc.poll())
        print 'received: ', [res]

def main():
    proc = Popen(['cat','-'],stdin=PIPE,stdout=PIPE)
    to_sed = proc.stdin
    from_sed = proc.stdout

    pid = os.fork()
    if pid == 0 :
        from_sed.close()
        produce(to_sed)
        return
    else :
        to_sed.close()
        consume(from_sed)

if __name__ == '__main__':
    main()

虽然这个解决方案在概念上非常容易理解,但它使用了一个以上的流程,并且与子流程模块相比停留在太低的级别上(这只是为了隐藏这类事情……)。

我想知道:是否有一个简单而干净的解决方案使用子流程模块,它不会挂起,或者要实现这种模式,我必须后退一步,实现一个老式的select循环或显式fork?

谢谢


Tags: 模块tofromimportforstdinstdoutsys
3条回答

如果不想将所有数据保存在内存中,则必须使用select。E、 例如:

import subprocess
from select import select
import os

proc = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

i = 0;
while True:
    rlist, wlist, xlist = [proc.stdout], [], []
    if i < 100000:
        wlist.append(proc.stdin)
    rlist, wlist, xlist = select(rlist, wlist, xlist)
    if proc.stdout in rlist:
        out = os.read(proc.stdout.fileno(), 10)
        print out,
        if not out:
            break
    if proc.stdin in wlist:
        proc.stdin.write('%d\n' % i)
        i += 1
        if i >= 100000:
            proc.stdin.close()

如果您想要一个纯Python解决方案,您需要将读写器放在一个单独的线程中。threading包是一种轻量级的方法,可以方便地访问公共对象,并且没有混乱的分叉。

import subprocess
import threading
import sys

proc = subprocess.Popen(['cat','-'],
                        stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE,
                        )
def writer():
    for i in range(100000):
        proc.stdin.write('%d\n' % i)
    proc.stdin.close()
thread = threading.Thread(target=writer)
thread.start()
for line in proc.stdout:
    sys.stdout.write(line)
thread.join()
proc.wait()

看到subprocess模块现代化,以支持流和协程,这可能是一件好事,这将允许混合Python片段和shell片段的管道被构建得更加优雅。

下面是我用来通过子进程加载6G mysql转储文件的东西。远离外壳=正确。不安全,开始浪费资源。

import subprocess

fhandle = None

cmd = [mysql_path,
      "-u", mysql_user, "-p" + mysql_pass],
      "-h", host, database]

fhandle = open(dump_file, 'r')
p = subprocess.Popen(cmd, stdin=fhandle, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

(stdout,stderr) = p.communicate()

fhandle.close()

相关问题 更多 >