subprocess.Popen.stdout - чтение стандартного вывода в реальном времени (снова)

Опять тот же вопрос.
Причина в том, что я все еще не могу заставить его работать, прочитав следующее:

В моем случае у меня есть консольное приложение, написанное на C, давайте возьмем, например, этот код в цикле:

tmp = 0.0;   
printf("\ninput>>"); 
scanf_s("%f",&tmp); 
printf ("\ninput was: %f",tmp); 

Он постоянно читает некоторые входные и записывает некоторые выходные данные.

Мой код Python для взаимодействия с ним следующий:

p=subprocess.Popen([path],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
p.stdin.write('12345\n')
for line in p.stdout: 
    print(">>> " + str(line.rstrip())) 
    p.stdout.flush() 

До сих пор, когда я читал форму p.stdout, она всегда ожидала завершения процесса и затем выводила пустую строку. Я много чего перепробовал - но результат тот же.

Я пробовал Python 2.6 и 3.1, но версия не имеет значения - мне просто нужно, чтобы он где-то работал.


person Vladimir Keleshev    schedule 29.06.2010    source источник
comment
Я в точно такой же ситуации, прочитав все эти вопросы и все еще не нашел ничего элегантного, что работает.   -  person Mike Chamberlain    schedule 08.03.2012
comment
Я пришел сюда с тем же вопросом, нашел здесь ответ stackoverflow.com/q/375427/168034   -  person phunehehe    schedule 16.10.2013


Ответы (5)


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

Для более скромных объемов данных может быть достаточно метода Popen.communicate(). Однако для данных, превышающих его буферизацию, вы, вероятно, столкнетесь с остановкой процессов (подобно тому, что вы уже видите?)

Возможно, вы захотите узнать подробности об использовании модуля fcntl и о том, как сделать один или другой (или оба) ваших файловых дескрипторов неблокирующими. В этом случае, конечно, вам придется заключить все чтения и / или записи в эти файловые дескрипторы в соответствующую обработку исключений для обработки событий «EWOULDBLOCK». (Я не помню точное исключение Python, которое возникло для них).

Совершенно другой подход заключается в том, что ваш родитель будет использовать модуль select и os.fork() ..., а дочерний процесс - execve() целевую программу после непосредственной обработки любого файла dup () ing. (По сути, вам придется повторно реализовать части Popen(), но с другой обработкой дескриптора родительского файла (PIPE).

Между прочим, .communicate, по крайней мере, в стандартных библиотеках Python 2.5 и 2.6, будет обрабатывать только около 64 КБ удаленных данных (в Linux и FreeBSD). Это число может варьироваться в зависимости от различных факторов (возможно, включая параметры сборки, используемые для компиляции вашего интерпретатора Python, или версию libc, с которой он связан). Он НЕ ограничивается просто доступной памятью (несмотря на утверждение Дж. Ф. Себастьяна об обратном), а ограничивается гораздо меньшим значением.

person Jim Dennis    schedule 29.06.2010
comment
Не могли бы вы привести пример реализации? Я просто немного поигрался с модулем Queue, но он вообще не работает. - person Philipp; 29.06.2010
comment
+1 за упоминание буферизации как основного виновника, см. Также Почему бы просто не использовать канал (popen ())? из pexpect docs. Но ответ слишком пессимистичен, т.е. .communicate() будет работать для любых данных, которые могут поместиться в памяти (намного больше, чем размеры буфера канала). Также fcntl (вероятно) не будет работать в Windows, да и в этом нет необходимости. - person jfs; 04.09.2013
comment
.communicate() действительно работает. В более ранних версиях, вероятно, была ошибка, которая сейчас исправлена. - person jfs; 06.09.2013

Переместите чтение из конвейера в отдельный поток, который сигнализирует, когда доступен фрагмент вывода:

Как можно Я прочитал все доступные данные из subprocess.Popen.stdout (без блокировки)?

person ddotsenko    schedule 29.06.2010

Аргумент bufsize=256 предотвращает отправку 12345\n дочернему процессу в блоке размером менее 256 байт, как это будет при опускании bufsize или вставке p.stdin.flush() после p.stdin.write(). Поведение по умолчанию - буферизация строк.

В любом случае вы должны увидеть хотя бы одну пустую строку перед блокировкой, выданной первым printf(\n...) в вашем примере.

person IBue    schedule 12.02.2013

Ваш конкретный пример не требует взаимодействия «в реальном времени». Следующие работы:

from subprocess import Popen, PIPE

p = Popen(["./a.out"], stdin=PIPE, stdout=PIPE)
output = p.communicate(b"12345")[0] # send input/read all output
print output,

где a.out - ваш пример программы C.

В общем, для диалогового взаимодействия с подпроцессом можно использовать модуль pexpect (или его аналоги в Windows):

import pexpect

child = pexpect.spawn("./a.out")
child.expect("input>>")
child.sendline("12345.67890") # send a number
child.expect(r"\d+\.\d+") # expect the number at the end
print float(child.after) # assert that we can parse it
child.close()
person jfs    schedule 04.09.2013

У меня была такая же проблема, и "proc.communicate ()" не решает ее, потому что ожидает завершения процесса.

Вот что у меня работает в Windows с Python 3.5.1:

import subprocess as sp
myProcess = sp.Popen( cmd, creationflags=sp.CREATE_NEW_PROCESS_GROUP,stdout=sp.PIPE,stderr=sp.STDOUT)
while i<40:
        i+=1
        time.sleep(.5)
        out = myProcess.stdout.readline().decode("utf-8").rstrip()

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

myProcess = sp.Popen( cmd, stdout=sp.PIPE)
for i in range(40)
            time.sleep(.5)
            out = myProcess.stdout.readline()
person 2diabolos.com    schedule 24.02.2016