我有一个非常简单的问题。我有一个大文件,经过三个步骤,使用外部程序的解码步骤,python中的一些处理,然后使用另一个外部程序重新编码。我一直在使用subprocess.Popen()来尝试在python中执行此操作,而不是形成unix管道。但是,所有数据都缓冲到内存中。有没有pythonic方式来完成这个任务,或者我最好回到一个简单的python脚本,它从stdin读取并写入stdout,两边都有unix管道?
import os, sys, subprocess
def main(infile,reflist):
print infile,reflist
samtoolsin = subprocess.Popen(["samtools","view",infile],
stdout=subprocess.PIPE,bufsize=1)
samtoolsout = subprocess.Popen(["samtools","import",reflist,"-",
infile+".tmp"],stdin=subprocess.PIPE,bufsize=1)
for line in samtoolsin.stdout.read():
if(line.startswith("@")):
samtoolsout.stdin.write(line)
else:
linesplit = line.split("\t")
if(linesplit[10]=="*"):
linesplit[9]="*"
samtoolsout.stdin.write("\t".join(linesplit))
答案 0 :(得分:8)
Popen有一个bufsize
参数,它将限制内存中缓冲区的大小。如果您根本不想要内存中的文件,则可以将文件对象作为stdin
和stdout
参数传递。来自subprocess docs:
bufsize,如果给定,与内置open()函数的对应参数具有相同的含义:0表示无缓冲,1表示行缓冲,任何其他正值表示使用(大约)该大小的缓冲区。负bufsize意味着使用系统默认值,这通常意味着完全缓冲。 bufsize的默认值为0(无缓冲)。
答案 1 :(得分:5)
尝试做出这个小改动,看效率是否更好。
for line in samtoolsin.stdout:
if(line.startswith("@")):
samtoolsout.stdin.write(line)
else:
linesplit = line.split("\t")
if(linesplit[10]=="*"):
linesplit[9]="*"
samtoolsout.stdin.write("\t".join(linesplit))
答案 2 :(得分:3)
但是,所有数据都缓冲到内存......
您使用的是subprocess.Popen.communicate()
吗?按照设计,此函数将等待进程完成,同时将数据累积到缓冲区中,然后将其返回给您。正如您所指出的,如果处理非常大的文件,这是有问题的。
如果要在生成数据时处理数据,则需要使用poll()
和.stdout.read()
方法编写循环,然后将该输出写入另一个套接字/文件/等。< / p>
请务必注意文档中的警告,防止这样做,因为很容易导致死锁(父进程等待子进程生成数据,而进程又等待父进程清空管道缓冲区。)
答案 3 :(得分:1)
我在stdout流上使用.read()方法。相反,我只需要在上面的for循环中直接从流中读取。纠正后的代码符合我的预期。
#!/usr/bin/env python import os import sys import subprocess def main(infile,reflist): print infile,reflist samtoolsin = subprocess.Popen(["samtools","view",infile], stdout=subprocess.PIPE,bufsize=1) samtoolsout = subprocess.Popen(["samtools","import",reflist,"-", infile+".tmp"],stdin=subprocess.PIPE,bufsize=1) for line in samtoolsin.stdout: if(line.startswith("@")): samtoolsout.stdin.write(line) else: linesplit = line.split("\t") if(linesplit[10]=="*"): linesplit[9]="*" samtoolsout.stdin.write("\t".join(linesplit))
答案 4 :(得分:-1)
尝试在python中使用非常大的输入执行一些基本的shell管道:
svnadmin load /var/repo < r0-100.dump
我发现即使使用大型(2-5GB)文件,最简单的方法就是:
subprocess.check_output('svnadmin load %s < %s' % (repo, fname), shell=True)
我喜欢这种方法,因为它很简单,你可以做标准的shell重定向。
我尝试使用Popen路线来运行重定向:
cmd = 'svnadmin load %s' % repo
p = Popen(cmd, stdin=PIPE, stdout=PIPE, shell=True)
with open(fname) as inline:
for line in inline:
p.communicate(input=line)
但这打破了大文件。使用:
p.stdin.write()
还打破了非常大的文件。