subprocess.Popen.stdout - чтение stdout в режиме реального времени (снова) - PullRequest
12 голосов
/ 29 июня 2010

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

В моем случае у меня есть консольное приложение, написанное на 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 ]

3 голосов
/ 29 июня 2010

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

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

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

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

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

1 голос
/ 12 февраля 2013

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

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

1 голос
/ 29 июня 2010

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

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

0 голосов
/ 24 февраля 2016

У меня была такая же проблема, и 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()
0 голосов
/ 04 сентября 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...