Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

У меня была та же проблема, и "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()