Как отмечает @ysth, причина, по которой вы не получаете никаких выходных данных, заключается в том, что STDOUT
и STDERR
процесса, соответствующего команде $cmd
, не являются буферизованными строками, а скорее буферизируются блоками. Таким образом, все выходные данные собираются в буфере, который не отображается (печатается) до тех пор, пока буфер не заполнится или не будет явно очищен. Однако, когда ваша команда истекает, все выходные данные все еще находятся в буфере и еще не были сброшены и, следовательно, собраны в переменную $out
в родительском процессе (сценарии).
Также обратите внимание, что поскольку ваш $cmd
сценарий является сценарием Perl, это поведение описано в perlvar
:
$ |
Если установлено значение, отличное от нуля, вызывает сброс сразу и после каждой записи
или распечатайте на текущем выбранном выходном канале. По умолчанию 0
(независимо от того, действительно ли канал буферизован системой или
не; $ | говорит только, просили ли вы Perl явно сбросить
после каждого пишу). STDOUT обычно будет буферизоваться строкой, если вывод
в терминал и блокировать буферизованный в противном случае .
Проблема (то, что программа не подключена к терминалу или tty) также отмечена на странице документации для IPC::Run
:
Интерактивные приложения обычно оптимизированы для использования человеком. Это может
помогать или мешать пытаться взаимодействовать с ними через такие модули, как
IPC :: Run. Часто программы изменяют свое поведение при обнаружении
что stdin, stdout или stderr не связаны с tty, если
что они запускаются в пакетном режиме. Помогает ли это или болит
зависит от того, какие оптимизации меняются. И часто нет никакого способа
рассказывать, что программа делает в этих областях, кроме проб и ошибок
и иногда, читая источник. Это включает в себя разные версии
и реализации одной и той же программы.
В документации также приведен список возможных обходных путей, включая использование псевдо-терминалов .
Одно из решений для вашего конкретного случая заключается в том, чтобы явно сделать STDOUT
строку буферизованной в начале вашего скрипта:
STDOUT->autoflush(1); # Make STDOUT line buffered
# Alternatively use: $| = 1;
for (my $i = 0; $i < 10; $i++) {
sleep 1;
print "Hello from script 1 " . localtime() . "\n";
}
Редактировать
Если по какой-то причине вы не можете изменить запущенные вами скрипты, вы можете попробовать подключить скрипт к псевдотерминалу. Таким образом, вместо вставки операторов типа STDOUT->autoflush(1)
в исходный код сценария, вы можете обмануть сценарий, полагая, что он подключен к терминалу и, следовательно, должен использовать буферизацию строки. Для вашего случая мы просто добавляем аргумент >pty>
перед аргументом \$out
в вызове harness
:
my $h = harness $cmd, \undef, '>pty>', \$out,
timeout(12, exception => {name => 'timeout'});
eval {
run $h;
};