2010-10-05 1 views
9

Я бег следующей версии Python:Как закрыть подпроцесс Python 2.5.2 Popen, когда у меня есть данные, которые мне нужны?

$ /usr/bin/env python --version                                        
Python 2.5.2          

Я бег следующего кода Python для записи данных из дочернего подпроцесса на стандартный вывод, и чтение, что в переменный Python под названием metadata:

# Extract metadata (snippet from extractMetadata.py) 
inFileAsGzip = "%s.gz" % inFile                                                    
if os.path.exists(inFileAsGzip):                                                   
    os.remove(inFileAsGzip)                                                     
os.symlink(inFile, inFileAsGzip)                                                   
extractMetadataCommand = "bgzip -c -d -b 0 -s %s %s" % (metadataRequiredFileSize, inFileAsGzip)                                    
metadataPipes = subprocess.Popen(extractMetadataCommand, stdin=None, stdout=subprocess.PIPE, shell=True, close_fds=True)                          
metadata = metadataPipes.communicate()[0]                                                                                                   
metadataPipes.stdout.close()                                                    
os.remove(inFileAsGzip) 
print metadata 

прецедент следующим образом, чтобы тянуть первые десять строк стандартного вывода из упомянутого выше фрагмента кода:

$ extractMetadata.py | head 

появится ошибка, если я труба в голову, AWK, Grep и т.д.

Сценарий заканчивается со следующей ошибкой:

close failed: [Errno 32] Broken pipe 

Я бы подумал, закрывая трубы будет достаточно, но, очевидно, это не так.

+1

Этот код отлично работает для меня после включения его использовать GZIP. Если вы просто распаковываете файлы gzip, почему вы используете неясный инструмент, о котором никто не знает («bgzip»)? Google считает, что на SF действует только летящий проект. Используйте zcat или, еще лучше, используйте модуль gzip. Вы вряд ли получите ответ на этот вопрос, если вы не предоставите образец кода, который воспроизводит проблему. –

+0

Я отредактировал вопрос, чтобы показать, что используется случай, который запускает сломанную трубу. Кодекс, который я предоставил, должен быть достаточным, я думаю. Если я заменил «bgzip» на «gzip», я получаю ту же ошибку. Инструмент «bgzip» является частью набора SAMtools, который включает модифицированную версию «gzip», которая выполняет произвольный доступ в архиве. Надеюсь это поможет. –

ответ

4

Хммм. Я видел некоторую странность «Сломанной трубы» с подпроцессом + gzip. Я никогда точно не выяснил, почему это происходит, но, изменив мой подход к реализации, я смог избежать проблемы. Похоже, вы просто пытаетесь использовать процесс back-gzip для распаковки файла (возможно, потому, что встроенный модуль Python ужасно медленный ... не знаю, почему, но это определенно есть).

Вместо того, чтобы использовать communicate(), вы можете обрабатывать процесс как полностью асинхронный сервер и просто читать его вывод по мере его поступления. Когда процесс умирает, модуль подпроцесса позаботится о том, чтобы очистить вещи для вас. Следующий снипп должен обеспечивать такую ​​же базовую функциональность без проблем с поврежденными трубами.

import subprocess 

gz_proc = subprocess.Popen(['gzip', '-c', '-d', 'test.gz'], stdout=subprocess.PIPE) 

l = list() 
while True: 
    dat = gz_proc.stdout.read(4096) 
    if not d: 
     break 
    l.append(d) 

file_data = ''.join(l) 
+0

Спасибо за ваш ответ. При таком подходе я все еще сталкиваюсь с обрывами трубных ошибок. Возможно, Popen() и write() плохо взаимодействуют в отношении вывода трубопровода в оболочку csh/bash. –

0

Недостаточно информации, чтобы ответить на это окончательно, но я могу сделать некоторые обоснованные предположения.

Во-первых, os.remove, безусловно, не должен терпеть неудачу с EPIPE. Не похоже, что это так; ошибка close failed: [Errno 32] Broken pipe, а не remove failed. Похоже, close терпит неудачу, а не remove.

Возможно, что вы можете закрыть трубку, чтобы дать эту ошибку. Если данные буферизованы, Python будет очищать данные перед закрытием файла. Если основной процесс исчез, это приведет к увеличению IOError/EPIPE. Однако учтите, что это не фатальная ошибка: даже когда это происходит, файл по-прежнему закрыт. Следующий код воспроизводит это примерно в 50% случаев и демонстрирует, что файл закрыт после исключения. (Остерегайтесь, я думаю, что поведение bufsize изменилось в разных версиях.)

import os, subprocess 
    metadataPipes = subprocess.Popen("echo test", stdin=subprocess.PIPE, 
     stdout=subprocess.PIPE, shell=True, close_fds=True, bufsize=4096) 
    metadataPipes.stdin.write("blah"*1000) 
    print metadataPipes.stdin 
    try: 
     metadataPipes.stdin.close() 
    except IOError, e: 
     print "stdin after failure: %s" % metadataPipes.stdin 

Это потрясающе; это происходит только часть времени. Это может объяснить, почему это похоже на удаление или добавление вызова os.remove влияет на ошибку.

Тем не менее, я не вижу, как это произойдет с кодом, который вы предоставили, поскольку вы не пишете на stdin. Это самое близкое, что я могу получить без полезного воспроизведения, и, возможно, он укажет вам в правильном направлении.

В качестве примечания стороны вы не должны проверять os.path.существует перед удалением файла, который может не существовать; это вызовет условия гонки, если другой процесс удалит файл одновременно. Вместо этого:

try: 
    os.remove(inFileAsGzip) 
except OSError, e: 
    if e.errno != errno.ENOENT: raise 

... который я обычно обернуть в функцию как rm_f.

Наконец, если вы явно хотите убить подпроцесс, есть metadataPipes.kill - просто закрыть его трубы не удастся - но это не поможет объяснить ошибку. Кроме того, если вы просто читаете gzip-файлы, вам намного лучше с gzip-модулем, чем подпроцессом. http://docs.python.org/library/gzip.html

+0

kill() не доступно в 2.5.2. Нет stdin; Я отредактировал вопрос, чтобы отразить это. Я не могу использовать модуль gzip для этой задачи, хотя использование двоичного кода gzip воспроизводит ошибки. –

+0

Теперь вопрос не имеет смысла: вы запускаете скрипт через 'head', но сценарий не имеет выхода. Просьба предоставить полный, самостоятельный репрограммирование; не заставляйте нас экспериментировать с частичным кодом, который даже не выполняется как есть, пытаясь угадать, о чем вы говорите. –

+0

Хорошо, неважно. Спасибо за вашу помощь. –

0

Получение первых 10 строк из вывода процесса может работать лучше так:

ph = os.popen(cmdline, 'r') 
lines = [] 
for s in ph: 
    lines.append(s.rstrip()) 
    if len(lines) == 10: break 
print '\n'.join(lines) 
ph.close() 
+0

Что делать, если я хочу обрабатывать стандартный вывод по-другому? Другими словами, вместо использования head я обрабатываю вывод в awk-скрипте. Я получаю разбитую трубку, когда я вывожу вывод где-то в другом месте. –

+0

Если моя вышеприведенная версия скрипта терпит неудачу при передаче через awk, ваша проблема не имеет ничего общего с подпроцессом. Что мне не хватает? – Vlad

1

Я думаю, что это исключение не имеет ничего общего с подпроцесса вызова, ни его файловых дескрипторов (после вызова общенияpopen объект не работает). Это, кажется, классическая проблема закрытия sys.stdout в трубе:

http://bugs.python.org/issue1596

Несмотря на то, 3-летнюю ошибку она не была решена. Поскольку sys.stdout.write(...), кажется, не поможет, вы можете прибегнуть к вызову более низкого уровня, попробуйте это:

os.write(sys.stdout.fileno(), metadata)