2010-04-07 2 views
7

Я хотел использовать эквивалент python для соединения некоторых команд оболочки в perl. Что-то вроде открытой версии python (PIPE, «команда |»).Python's Popen cleanup

Я иду к модулю подпроцесса и попробовать это:

p = subprocess.Popen("zgrep thingiwant largefile", shell=True, stdout=subprocess.PIPE) 

Это работает для чтения выходного так же, как я бы в Perl, но она не очиститься. Когда я выйти из интерпретатора, я получаю

grep: writing output: Broken pipe 

изрыгают всего несколько стандартный вывод миллионов раз. Наверное, я наивно надеялся, что все это позаботится обо мне, но это неправда. Вызов прекращения или убийства на p, похоже, не помогает. Посмотрите на таблицу процессов, я вижу, что это убивает процесс/bin/sh, но оставляет ребенка gzip на месте, чтобы жаловаться на сломанную трубу.

Каков правильный способ сделать это?

+1

Вы покидаете интерпретатор до того, как закончите свой подпроцесс 'p'? – physicsmichael

ответ

9

Вопрос в том, что pipe полон. Подпроцесс останавливается, ожидая, пока труба выйдет из строя, но затем ваш процесс (интерпретатор Python) завершает работу, разбивая его конец (следовательно, сообщение об ошибке).

p.wait() не поможет:

Предупреждение Это тупик, если дочерний процесс генерирует достаточно вывод на стандартный вывод или Stderr трубы таким образом, что он блокирует ожидание буфера OS трубы, чтобы принять больше данных. Используйте communicate(), чтобы этого избежать.

http://docs.python.org/library/subprocess.html#subprocess.Popen.wait

p.communicate() не поможет:

Примечания чтения данных в буфере в памяти, поэтому не используйте этот метод, если размер данных является большим или неограниченным.

http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate

p.stdout.read(num_bytes) не поможет:

Предупреждение Используйте communicate() вместо .stdin.write, .stdout.read или .stderr.read избежать тупиков в связи с какой-либо из других буферов OS труб наполнения и блокирование дочернего процесса.

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

Мораль рассказа, для большого выхода, subprocess.PIPE обречет вас на какой-либо сбой, если ваша программа пытается прочитать данные (мне кажется, что вы должны быть в состоянии поставить p.stdout.read(bytes) в цикл while p.returncode is None:, но указанное выше предупреждение указывает на то, что это может привести к блокировке).

Документов предлагают замены оболочки трубы с этим:

p1 = Popen(["zgrep", "thingiwant", "largefile"], stdout=PIPE) 
p2 = Popen(["processreceivingdata"], stdin=p1.stdout, stdout=PIPE) 
output = p2.communicate()[0] 

Обратите внимание, что p2 принимают стандартный ввод непосредственно из p1. Это должно избежать взаимоблокировок, но с учетом противоречивых предупреждений выше, кто знает.

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

0

Как вы выполнили этот процесс?

Правильный способ заключается в использовании

p.communicate() 

См документы для получения более подробной информации.

+0

Это происходит, даже если я никогда не общаюсь с процессом. Простое создание объекта p, а затем выход из интерпретатора вызывает эту проблему. –

+0

Да, если я правильно помню, Popen выполняет команду. 'общаться()' затем ждет, пока процесс не закончится, буферы будут сброшены и т. д. и т. д. Также см. 'check_call()'. – Almad

2

После открытия трубы, вы можете работать с выводом команды: p.stdout:

for line in p.stdout: 
    # do stuff 
p.stdout.close() 
0

Вы должны wait для завершения процесса:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True) 
p.wait() 

В качестве альтернативы, вы можете захватить (как у вас есть), и, возможно, его стандартную ошибку, а затем позвоните по телефону communicate:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
stdout, stderr = p.communicate()