Python 2 Подпроцесс: невозможно получить вывод из readline - PullRequest
1 голос
/ 16 января 2020

У меня есть следующее C приложение

#include <stdio.h>

int main(void)
{
    printf("hello world\n");
    /* Go into an infinite loop here. */
    while(1);

    return 0;
}

И у меня есть следующий python код.

import subprocess
import time
import pprint


def run():
    command = ["./myapplication"]
    process = subprocess.Popen(command, stdout=subprocess.PIPE)
    try:
        while process.poll() is None:
            # HELP: This call blocks...
            for i in  process.stdout.readline():
                print(i)

    finally:
        if process.poll() is None:
            process.kill()


if __name__ == "__main__":
    run()

Когда я запускаю код python, стандартный вывод .readline или даже блоки stdout.read.

Если я запускаю приложение, используя subprocess.call (программу), тогда я вижу «hello world» в stdout.

Как я могу прочитать ввод из stdout с примером, который я привел?

Примечание: я не хотел бы изменять мою программу C. Я пробовал это на Python 2.7.17 и Python 3.7.5 под Ubuntu 19.10, и у меня такое же поведение. Добавление bufsize = 0 мне не помогло.

Ответы [ 2 ]

3 голосов
/ 20 февраля 2020

Самый простой способ - использовать буферы sh в программе C

...
printf("hello world\n");
fflush(stdout);
while(1);
...

Если вы не хотите изменять программу C, вы можете манипулировать библиотекой lib c буферное поведение извне. Это можно сделать с помощью stdbuf для вызова вашей программы (linux). Синтаксис "stdbuf -o0 yourapplication" для буферизации нуля и "stdbuf -oL yourapplication" для буферизации строки. Поэтому в вашем python коде используйте

...
command = ["/usr/bin/stdbuf","-oL","pathtomyapplication"]
process = subprocess.Popen(command, stdout=subprocess.PIPE)
...

или

...
command = ["/usr/bin/stdbuf","-o0","pathtomyapplication"]
process = subprocess.Popen(command, stdout=subprocess.PIPE)
...
2 голосов
/ 20 февраля 2020

Приложения, созданные с использованием буфера ввода и вывода буфера C Standard IO (построена с #include <stdio.h>) (см. здесь , почему). Библиотека stdio, такая как isatty, может сказать, что она пишет в канал, а не в TTY, и поэтому она выбирает буферизацию блоков вместо буферизации строк. Данные сбрасываются, когда буфер заполнен, но "hello world\n" не заполняет буфер, поэтому он не сбрасывается.

В ответе Тимо Хартманн , с использованием * показан обратный путь 1013 * утилита. При этом используется трюк LD_PRELOAD для замены собственного libstdbuf.so. Во многих случаях это хорошее решение, но LD_PRELOAD является своего рода хаком, а не работает в некоторых случаях , поэтому это может не быть общим решением.

Может быть, вы хотите чтобы сделать это непосредственно в Python, и здесь вам могут помочь опции stdlib, вы можете использовать pseudo-tty ( docs py2 , docs py3 ) подключен к stdout вместо трубы . Программа myapplication должна включить буферизацию строки, что означает, что любой символ новой строки очищает буфер.

from __future__ import print_function
from subprocess import Popen, PIPE
import errno
import os
import pty
import sys

mfd, sfd = pty.openpty()
proc = Popen(["/tmp/myapplication"], stdout=sfd)
os.close(sfd)  # don't wait for input
while True:
    try:
        output = os.read(mfd, 1000)
    except OSError as e:
        if e.errno != errno.EIO:
            raise
    else:
        print(output)

Обратите внимание, что мы теперь читаем байтов из вывода сейчас, поэтому мы не можем обязательно расшифруйте их прямо сейчас!

См. Обработка вывода подпроцесса с Python в реальном времени для сообщения в блоге, очищающего эту идею. Существуют также сторонние библиотеки для этого, см. ptyprocess .

...