Tim*_*Tim 31 python subprocess
我有一些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))
Run Code Online (Sandbox Code Playgroud)
文档中的评论似乎表明了潜在的问题.等待,有:
警告:如果子进程生成足够的输出
stdout或stderr管道,以阻止等待OS管道缓冲区接受更多数据,这将导致死锁.使用communicate()以避免这种情况.
虽然在沟通中,我看到:
注意读取的数据缓冲在内存中,因此如果数据大小很大或不受限制,请不要使用此方法.
因此我不清楚如果我有大量数据,我应该使用其中任何一种.它们没有说明在这种情况下我应该使用什么方法.
我确实需要来自exec的返回值并进行解析并使用stdout和stderr.
那么Python中用于执行具有大输出的外部应用程序的等效方法是什么?
Gle*_*ard 18
你正在阻止对两个文件的读取; 第一个需要在第二个开始之前完成.如果应用程序写了很多内容stderr,而没有任何内容stdout,那么你的进程将等待未来的数据stdout,而你正在运行的程序就在那里等待它写入的内容stderr被读取(它永远不会会 - 因为你在等待stdout).
有几种方法可以解决这个问题.
最简单的是不拦截stderr; 离开stderr=None.错误将直接输出stderr.您无法拦截它们并将它们显示为您自己的消息的一部分.对于命令行工具,这通常没问题.对于其他应用程序,它可能是一个问题.
另一种简单的方法是重定向stderr到stdout,因此您只有一个传入文件:set stderr=STDOUT.这意味着您无法区分常规输出和错误输出.取决于应用程序如何写入输出,这可能是也可能是不可接受的.
完整而复杂的处理方法是select(http://docs.python.org/library/select.html).这使您可以以非阻塞方式读取:只要数据出现在stdout或,就可以获得数据stderr.如果真的有必要,我只会推荐这个.这可能在Windows中不起作用.
使用以下方法读取stdout并stderr独立使用非常大的输出(即大量兆字节)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)
Run Code Online (Sandbox Code Playgroud)
很多输出都是主观的,因此提出建议有点困难.如果输出量非常大,那么您可能不希望通过单个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
Run Code Online (Sandbox Code Playgroud)
格伦梅纳德在关于僵局的评论中是正确的.但是,解决这个问题的最好方法是创建两个线程,一个用于stdout,一个用于stderr,它们读取相应的流直到耗尽,并根据输出执行任何操作.
根据输出的大小等因素以及是否需要在生成时处理子进程的输出,使用临时文件的建议可能适用于您,也可能不适用.
正如Heikki Toivonen建议的那样,你应该看看这个communicate方法.但是,这会将子进程的stdout/stderr缓存在内存中,并从communicate调用中返回- 这对于某些情况并不理想.但是沟通方法的来源值得关注.
另一个例子是我维护的包python-gnupg,其中gpg可执行文件通过生成subprocess来完成繁重的工作,并且Python包装器生成线程来读取gpg的stdout和stderr并在gpg生成数据时使用它们.您也可以通过查看源代码来获得一些想法.在一般情况下,gpg对stdout和stderr生成的数据可能非常大.
小智 5
我有同样的问题。如果您必须处理大量输出,另一个不错的选择可能是为 stdout 和 stderr 使用一个文件,并为每个参数传递这些文件。
检查 python 中的临时文件模块:https://docs.python.org/2/library/tempfile.html。
像这样的事情可能会奏效
out = tempfile.NamedTemporaryFile(delete=False)
Run Code Online (Sandbox Code Playgroud)
那么你会这样做:
Popen(... stdout=out,...)
Run Code Online (Sandbox Code Playgroud)
然后您可以读取该文件,并稍后将其擦除。
| 归档时间: |
|
| 查看次数: |
19801 次 |
| 最近记录: |