subprocess.Popen.stdout - чтение 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, но версия не имеет значения - мне просто нужно, чтобы она где-то работала.

5 ответов

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

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

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

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

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

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

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

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

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

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

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()

У меня была такая же проблема, и 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()
Другие вопросы по тегам