Получение вывода другой программы в качестве ввода на лету - PullRequest
10 голосов
/ 11 сентября 2009

Я использую две программы таким образом:

$ c_program | python_program.py

c_program печатает что-то, используя printf(), а python_program.py читает, используя sys.stdin.readline()

Я бы хотел, чтобы вывод процесса c_program процесса python_program.py выполнялся сразу после печати, чтобы он мог выводить свой собственный текущий вывод. К сожалению, python_program.py получает свои данные только после завершения c_program.

Как я могу решить это?

Ответы [ 6 ]

17 голосов
/ 11 сентября 2009

Просто установите stdout для буферизации строки в начале вашей программы на C (перед выполнением какого-либо вывода), например:

#include <stdio.h>
setvbuf(stdout, NULL, _IOLBF, 0);

или

#include <stdio.h>
setlinebuf(stdout);

Любой из них будет работать в Linux, но setvbuf является частью стандарта C, поэтому он будет работать на большем количестве систем.

По умолчанию стандартный вывод будет буферизироваться блоком для канала или файла, или буферизироваться строкой для терминала. Так как в этом случае stdout является конвейером, по умолчанию будет буферизироваться блок. Если это буферизованный блок, то буфер будет очищен, когда он заполнится или когда вы вызовете fflush(stdout). Если она буферизована, то она будет автоматически очищаться после каждой строки.

8 голосов
/ 11 сентября 2009

Вам нужно, чтобы ваша программа на C вызывала fflush (stdout) после каждой строки. Например, с помощью инструмента GNU grep вы можете вызвать опцию '--line-buffered', которая вызывает такое поведение. См. fflush .

7 голосов
/ 11 сентября 2009

Если вы можете изменить свою программу на C, вы уже получили ответ , но я подумал, что я бы включил решение для тех, кто не может / не будет изменять код.

ожидаемо имеет пример сценария под названием unbuffer , который сделает свое дело.

1 голос
/ 11 сентября 2009

Возможно, вы захотите попробовать flush в потоке stdout в программе cpp.

1 голос
/ 11 сентября 2009

Все оболочки Unix (которые я знаю) реализуют конвейеры оболочки через что-то другое, чем pty (как правило, они используют каналы Unix! -); следовательно, библиотека времени выполнения C / C ++ в cpp_program ЗНАЕТ, что ее вывод НЕ является терминалом, и, следовательно, она будет буферизовать вывод (кусками по несколько КБ за раз). Если вы не напишите свою собственную оболочку (или semiquasimaybeshelloid), которая реализует конвейеры с помощью pyt, я считаю, что нет способа сделать то, что вам нужно, используя конвейерную нотацию.

Рассматриваемая вещь "шеллоид" может быть написана на Python (или на C, или на Tcl, или ...) с использованием модуля pty стандартной библиотеки или высокоуровневой абстракции на ее основе, такой как pexpect , и тот факт, что две программы, которые должны быть соединены через "конвейер на основе pty", написаны на C ++ и Python, довольно не имеет значения. Ключевая идея состоит в том, чтобы обмануть программу слева от канала и заставить ее поверить, что ее стандартный вывод является терминалом (поэтому pty должен быть в корне этого трюка), чтобы обмануть свою библиотеку времени выполнения в НЕ буферизировать вывод. После того, как вы написали такой шеллоид, вы бы назвали его с некоторым синтаксисом, таким как:

$ shelloid 'cpp_program | python_program.py '

Конечно, было бы проще обеспечить «точечное решение», написав python_program в знании, что он должен порождать cpp_program как подпроцесс И обманом заставить его поверить, что его стандартный вывод является терминалом (т. Е. * Например, 1013 * будет напрямую использовать pexpect). Но если у вас миллион таких ситуаций, когда вы хотите отменить обычную буферизацию, выполняемую предоставляемой системой библиотекой времени выполнения C, или во многих случаях, когда вы хотите повторно использовать существующие фильтры и т. Д., На самом деле предпочтительным может быть написание shelloid.

0 голосов
/ 11 сентября 2009

хорошо, это может звучать глупо, но это может сработать:

вывод вашего pgm в файл

$ c_program >> ./out.log

разработать программу на python, которая читает команду tail

import os

tailoutput = os.popen("tail -n 0 -f ./out.log")

try:
    while 1:
        line = tailoutput.readline()
        if len(line) == 0:
            break

        #do the rest of your things here
        print line

except KeyboardInterrupt:
        print "Quitting \n"
...