使用subprocess.Popen与大输出过程(Using subprocess.Popen fo

2019-06-17 15:06发布

我有一个执行时,应用程序具有输出少量的正常工作的外部应用程序,但是当有很多挂着一些Python代码。 我的代码如下所示:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errcode = p.wait()
retval = p.stdout.read()
errmess = p.stderr.read()
if errcode:
    log.error('cmd failed <%s>: %s' % (errcode,errmess))

有在文档似乎表明潜在问题的意见。 在等待中,有:

警告:如果子进程产生足够的输出到这将死锁stdoutstderr管道,使得它阻止等待操作系统管缓冲区,接受更多的数据。 使用communicate()来避免这种情况。

虽然在沟通,我看到:

注意数据读取缓存在内存中,因此,如果数据量很大或无限不要使用此方法。

因此,目前还不清楚,我认为我应该使用这些如果我有大量的数据。 它们并不表示我应该在这种情况下,用什么方法。

我确实需要从EXEC返回值和做解析,并使用这两个stdoutstderr

那么,什么是Python中的等效方法给exec那都将有较大的输出外部应用程序?

Answer 1:

你正在做阻塞读取两个文件; 第一,需要在第二个开始前完成。 如果应用程序写了很多stderr ,并没有什么stdout ,那么你的程序将坐在等待数据stdout是不来了,当你运行的程序坐在那里等待它写信给东东stderr被读取(它永远不会-因为你在等待stdout )。

有您能解决这个问题的一些方法。

最简单的是不拦截stderr ; 离开stderr=None 。 错误将被输出到stderr直接。 你不能拦截下来,​​并显示它们作为自己消息的一部分。 对于命令行工具,这往往是OK。 对于其他的应用程序,它可以是一个问题。

另一个简单的办法是重定向stderrstdout ,所以你只能有一个传入文件:设置stderr=STDOUT 。 这意味着你不能从错误输出区分常规输出。 这可能是或可能不是可接受的,这取决于应用程序如何将输出写入。

处理这种情况的完整和复杂的方式是select ( http://docs.python.org/library/select.html )。 这可让您在非阻塞的方式阅读:你得到的数据,只要数据上会出现两种stdoutstderr 。 我只建议,如果真的有必要。 这可能不会在Windows中运行。



Answer 2:

阅读stdoutstderr具有非常大的独立输出(即,许多兆字节)使用select

import subprocess, select

proc = subprocess.Popen(cmd, bufsize=8192, shell=False, \
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

with open(outpath, "wb") as outf:
    dataend = False
    while (proc.returncode is None) or (not dataend):
        proc.poll()
        dataend = False

        ready = select.select([proc.stdout, proc.stderr], [], [], 1.0)

        if proc.stderr in ready[0]:
            data = proc.stderr.read(1024)
            if len(data) > 0:
                handle_stderr_data(data)

        if proc.stdout in ready[0]:
            data = proc.stdout.read(1024)
            if len(data) == 0: # Read of zero bytes means EOF
                dataend = True
            else:
                outf.write(data)


Answer 3:

大量的输出是主观所以这是一个有点困难提出建议。 如果输出量真的很大,那么你可能不想与一个read()调用反正抓住这一切。 你可能想尝试写输出到一个文件,然后拉递增像这样的数据:

f=file('data.out','w')
p = subprocess.Popen(cmd, shell=True, stdout=f, stderr=subprocess.PIPE)
errcode = p.wait()
f.close()
if errcode:
    errmess = p.stderr.read()
    log.error('cmd failed <%s>: %s' % (errcode,errmess))
for line in file('data.out'):
    #do something


Answer 4:

格伦·梅纳德是正确的,他对死锁评论。 然而,解决这一问题的最好办法是二创建两个线程,一个用于标准输出,另一个用于标准错误,它读取这些相应的流,直到筋疲力尽,做任何你需要的输出。

使用临时文件的建议可能会或可能不是你随输出等的大小以及是否需要,因为它是生成处理子输出工作。

作为海基托伊沃宁曾建议,你应该看看的communicate方法。 但是,这种缓存在内存中的子进程的标准输出/标准错误,你会得到那些从返回的communicate电话-这是不理想的一些场景。 但是,沟通方法的来源是值得看的。

另一个例子是在一个包我维持, 蟒-gnupg的 ,其中gpg可执行经由催生subprocess做繁重和Python包装生成线程来读取GPG的输出和错误并作为数据被GPG产生消耗他们。 您可以通过查看源那里得到一些想法,以及。 通过GPG到两个输出和错误产生的数据可以是相当大的,在一般情况下。



Answer 5:

你可以尝试沟通,看看是否能解决您的问题。 如果不是这样,我将输出重定向到一个临时文件。



Answer 6:

我有同样的问题。 如果你要处理大量的输出,另一个很好的选择可能是使用stdout和stderr文件,并通过每个参数的文件。

检查蟒蛇的临时文件模块: https://docs.python.org/2/library/tempfile.html 。

像这样的东西可能会奏效

out = tempfile.NamedTemporaryFile(delete=False)

然后,你会怎么做:

Popen(... stdout=out,...)

然后你就可以读取该文件,并且在以后去掉它。



Answer 7:

这里是一个同时捕捉常规输出加上错误输出,都在这样的Python中的局限性简单的方法stdout并不适用:

com_str = 'uname -a'
command = subprocess.Popen([com_str], stdout=subprocess.PIPE, shell=True)
(output, error) = command.communicate()
print output

Linux 3.11.0-20-generic SMP Fri May 2 21:32:55 UTC 2014 

com_str = 'id'
command = subprocess.Popen([com_str], stdout=subprocess.PIPE, shell=True)
(output, error) = command.communicate()
print output

uid=1000(myname) gid=1000(mygrp) groups=1000(cell),0(root)


文章来源: Using subprocess.Popen for Process with Large Output