Очень большой ввод и передача с использованием подпроцесса. Открыть
У меня довольно простая проблема. У меня есть большой файл, который проходит три этапа: этап декодирования с использованием внешней программы, некоторую обработку в python, а затем перекодирование с использованием другой внешней программы. Я использовал subprocess.Popen(), чтобы попытаться сделать это в Python, а не формировать каналы Unix. Однако все данные буферизируются в памяти. Есть ли питонический способ решения этой задачи, или мне лучше вернуться к простому скрипту на 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))
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))
Попен имеет bufsize
параметр, который будет ограничивать размер буфера в памяти. Если вам вообще не нужны файлы в памяти, вы можете передать файловые объекты как stdin
а также stdout
параметры. Из документов подпроцесса:
bufsize, если он задан, имеет то же значение, что и соответствующий аргумент встроенной функции open(): 0 означает небуферизованный, 1 означает буферизацию строки, любое другое положительное значение означает использование буфера (приблизительно) этого размера. Отрицательный размер буфера означает использование системного значения по умолчанию, что обычно означает полную буферизацию. Значение по умолчанию для bufsize - 0 (без буферизации).
Тем не менее, все данные буферизируются в память...
Ты используешь subprocess.Popen.communicate()
? По своей задумке эта функция будет ожидать завершения процесса, все время накапливая данные в буфере, и затем возвращать их вам. Как вы указали, это проблематично, если вы имеете дело с очень большими файлами.
Если вы хотите обработать данные во время их генерации, вам нужно написать цикл, используя poll()
а также .stdout.read()
методы, а затем записать этот вывод в другой сокет / файл / и т. д.
Обязательно обратите внимание на предупреждения в документации против этого, поскольку это легко может привести к взаимоблокировке (родительский процесс ожидает, пока дочерний процесс сгенерирует данные, который, в свою очередь, ожидает, пока родительский процесс очистит буфер канала),
Я использовал метод.read () в потоке stdout. Вместо этого мне просто нужно было читать прямо из потока в цикле for выше. Исправленный код делает то, что я ожидал.
#! / usr / bin / env python импорт ОС импорт системы подпроцесс импорта def main(infile,reflist): печать инфиле, рефлист samtoolsin = subprocess.Popen(["samtools","view",infile], стандартный вывод =subprocess.PIPE, BUFSIZE =1) samtoolsout = subprocess.Popen(["samtools","import",reflist,"-", входной_файл +"TMP"], STDIN =subprocess.PIPE, BUFSIZE = 1) для строки в samtoolsin.stdout: если (line.startswith("@")): samtoolsout.stdin.write(линия) еще: linesplit = line.split("\t") если (linesplit[10]=="*"): linesplit[9]="*" samtoolsout.stdin.write("\ т".join(linesplit))
Попытка сделать некоторые основные оболочки оболочки с очень большим вводом в Python:
svnadmin load /var/repo < r0-100.dump
Я нашел самый простой способ заставить это работать даже с большими (2-5 ГБ) файлами:
subprocess.check_output('svnadmin load %s < %s' % (repo, fname), shell=True)
Мне нравится этот метод, потому что он прост, и вы можете сделать стандартное перенаправление оболочки.
Я попытался пойти по маршруту Попен, чтобы запустить перенаправление:
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()
Также сломался с очень большими файлами.