Сбросить вывод дочернего процесса - PullRequest
0 голосов
/ 05 февраля 2019

Я создал дочерний процесс через IPC::Open2.
Мне нужно читать строку stdout этого дочернего процесса построчно.
Проблема в том, что stdout дочернего процесса не подключен к терминалу, он полностью буферизован, и я не могу читать с него, пока процесс не завершится.

Как очистить вывод дочернего процесса без изменения его кода?


код дочернего процесса

while (<STDIN>) {
    print "Received : $_";
}

код родительского процесса:

use IPC::Open2;
use Symbol;

my $in = gensym();
my $out = gensym();

my $pid = open2($out, $in, './child_process');

while (<STDIN>) {
    print $in $_;
    my $line = <$out>;
    print "child said : $line";
}

Когда я запускаю код, он застревает в ожидании вывода дочернего процесса .
Однако, если я запускаю его с bc, результат, который я ожидаю, я считаю, bc должен вручную сбросить вывод

примечание:
В дочернем процессе, если я добавлю $| = 1 в начале или STDOUT->flush() после печати, родительский процесс сможет правильно прочитать его.
Однако это пример, и я должен работать с программами, которыене сбрасывать вручную вывод.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Одним из способов является настройка терминальной среды для процесса, псевдотерминала (pty).Это трудно сделать правильно и очень зависит от системы, но IPC :: Run имеет эту возможность, готовую к простому использованию.

Вот драйвер, работающий с использованием at, так чтоон не имеет управляющего терминала (или запускает его через cron)

use warnings;
use strict;
use feature 'say';

use IPC::Run qw(run);

my @cmd = qw(./t_term.pl input arguments); 

run \@cmd, '>pty>', sub { say "out: @_" };

#run \@cmd, '>', sub { say "out: @_" }   # no pty

При >pty> он устанавливает псевдотерминал для STDOUT программы в @cmd (это канал с>);также см. <pty< и подробнее о перенаправлении .Анонимный sub {} вызывается каждый раз, когда выводится дочерний процесс, поэтому его можно обрабатывать по ходу дела.Для этого есть и другие варианты.

Программа, которая называется (t_term.pl), тестирует только для терминала

use warnings;
use strict;
use feature 'say';

say "Is STDOUT filehandle attached to a terminal: ",
    ( (-t STDOUT) ? "yes" : "no" );
sleep 2;
say "bye from $$";

-t STDOUT (см. операторы filetest) является подходящим способом проверки терминала в этом примере.Подробнее / другие способы см. в этом посте .

. Вывод показывает, что вызываемая программа (t_term.pl) видит терминал на своем STDOUT, даже если драйвер работает без такового.(используя at или когда закончится crontab).Если >pty> изменяется на обычное перенаправление с помощью > (с использованием канала), то терминала нет.

Решает ли это проблему с буферизацией, явно зависит от этой программы, и достаточно ли этогообмануть его с помощью терминала.

Другой способ решения этой проблемы - использовать unbuffer, когда это возможно, как в ответе моба.

0 голосов
/ 05 февраля 2019

К сожалению, Perl не контролирует поведение буферизации программ, которые он выполняет.В некоторых системах есть утилита unbuffer, которая может сделать это.Если у вас есть доступ к этому инструменту, вы можете сказать:

my $pid = open2($out, $in, 'unbuffer ./child_process');

Здесь обсуждается об эквивалентных инструментах для Windows, но я не могу сказать, эффективны ли какие-либо из них.

...