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

Обход буферизации вывода подпроцесса с помощью popen в C или Python

У меня есть общий вопрос о popen (и всех связанных функциях), применимый ко всем операционным системам, когда я пишу python script или некоторый c-код и запускаю полученный исполняемый файл из консоли (win или linux), я может сразу увидеть результат процесса. Тем не менее, если я запускаю тот же исполняемый файл, что и разветвленный процесс, а его stdout перенаправляется в канал, выходные буферы где-то, обычно до 4096 байт, прежде чем он будет записан в канал, где родительский процесс может его прочитать.

Следующий python script будет генерировать вывод в кусках 1024 байта

import os, sys, time

if __name__ == "__main__":
     dye = '@'*1024
     for i in range (0,8):
        print dye
        time.sleep(1)

Следующий python script выполнит предыдущий script и прочитает вывод, как только он поступит в трубу, байт по байт

import os, sys, subprocess, time, thread

if __name__ == "__main__":
    execArgs = ["c:\\python25\\python.exe", "C:\\Scripts\\PythonScratch\\byte_stream.py"]

    p = subprocess.Popen(execArgs, bufsize=0, stdout=subprocess.PIPE)
    while p.returncode == None:
        data = p.stdout.read(1)
        sys.stdout.write(data)
        p.poll()

Отрегулируйте путь для вашей операционной системы. При запуске в этой конфигурации вывод не будет отображаться в кусках 1024, но куски 4096, несмотря на то, что размер буфера команды popen равен 0 (что по умолчанию все равно). Может ли кто-нибудь сказать мне, как изменить это поведение?, можно ли каким-либо образом заставить операционную систему обрабатывать вывод из разветвленного процесса так же, как когда он запускается с консоли?, т.е. Просто кормить данные через без буферизации?

4b9b3361

Ответ 1

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

Если вы контролируете программу, выполняющую запись, вы можете (как еще один ответ предлагал) постоянно поддерживать поток flush или (более элегантно, если это возможно), пытаться заставить stdout быть небуферизованным, например. запуская Python с флагом командной строки -u:

-u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
         see man page for details on internal buffering relating to '-u'

(то, что добавляет страница man, упоминается stdin и проблемы с двоичным режимом [s]).

Если вы не можете или не хотите прикасаться к программе, которая пишет, -u или тому подобное в программе, которую просто чтение вряд ли поможет (самая важная буферизация - это то, что происходит на сценаторе stdout, а не тот, на читателя stdin). Альтернатива заключается в том, чтобы обмануть автора, полагая, что он пишет терминал (хотя на самом деле он пишет в другую программу!), Через стандартный библиотечный модуль pty или стороннюю сторону более высокого уровня pexpect (или для Windows, его порт wexpect).

Ответ 2

Это правильно и относится как к Windows, так и к Linux (и, возможно, к другим системам), с popen() и fopen(). Если вы хотите, чтобы выходной буфер отправлялся до 4096 байт, используйте fflush() (на C) или sys.stdout.flush() (Python).