Благодаря рекомендациям @ikegami я обнаружил, что лучшим выбором для интерактивного чтения и записи в другой процесс в Perl является IPC::Run. Однако для этого требуется, чтобы программа, из которой вы читаете и пишете, имела известный вывод, когда она завершает запись в свой STDOUT, например приглашение. Вот пример, который выполняет bash
, запускает ls -l
, а затем печатает этот вывод:
use v5.14;
use IPC::Run qw(start timeout new_appender new_chunker);
my @command = qw(bash);
# Connect to the other program.
my ($in, @out);
my $ipc = start \@command,
'<' => new_appender("echo __END__\n"), \$in,
'>' => new_chunker, sub { push @out, @_ },
timeout(10) or die "Error: $?\n";
# Send it a command and wait until it has received it.
$in .= "ls -l\n";
$ipc->pump while length $in;
# Wait until our end-of-output string appears.
$ipc->pump until @out && @out[-1] =~ /__END__\n/m;
pop @out;
say @out;
Поскольку он работает как IPC (я предполагаю), bash
не выдает подсказку, когда завершает запись в свой STDOUT. Поэтому я использую функцию new_appender()
, чтобы она выдавала что-то, что я могу сопоставить, чтобы найти конец вывода (путем вызова echo __END__
). Я также использовал анонимную подпрограмму после вызова new_chunker
для сбора вывода в массив, а не в скаляр (просто передайте ссылку на скаляр в '>'
, если хотите).
Так что это работает, но, на мой взгляд, это отстой по целому ряду причин:
- Как правило, нет полезного способа узнать, что программа, управляемая IPC, закончила печать на свой STDOUT. Вместо этого вы должны использовать регулярное выражение на его выходе для поиска строки, которая обычно означает, что это сделано.
- Если он не выдает его, вы должны заставить его выдать один (как я сделал здесь — не дай бог, если бы у меня был файл с именем
__END__
). Если бы я управлял клиентом базы данных, мне, возможно, пришлось бы отправить что-то вроде SELECT 'IM OUTTA HERE';
. Для разных приложений потребуются разные new_appender
приемы.
- Запись магических скаляров
$in
и $out
кажется странной и похожей на действие на расстоянии. Мне это не нравится.
- Нельзя выполнять линейно-ориентированную обработку скаляров, как если бы они были дескрипторами файлов. Поэтому они менее эффективны.
- Возможность использовать
new_chunker
для получения линейного вывода хороша, хотя и немного странна. Тем не менее, это восстанавливает некоторую эффективность при чтении вывода программы, если предположить, что он эффективно буферизуется IPC::Run.
Теперь я понимаю, что, хотя интерфейс для IPC::Run потенциально мог бы быть немного лучше, общие недостатки модели IPC, в частности, делают ее сложной для решения. Не существует универсально полезного интерфейса IPC, потому что нужно слишком много знать о специфике конкретной выполняемой программы, чтобы заставить ее работать. Это нормально, возможно, если вы точно знаете, как он будет реагировать на входные данные, и сможете надежно распознать, когда он закончит выдачу вывода, и вам не нужно сильно беспокоиться о межплатформенной совместимости. Но этого было далеко недостаточно для моей потребности в общем полезном способе взаимодействия с различными клиентами командной строки базы данных в модуле CPAN, который можно было бы распространять на целый ряд операционных систем.
Наконец, благодаря предложениям по упаковке в комментариях к сообщению в блоге я решил отказаться от использования IPC для управления этими клиентами и вместо этого использовать DBI. Он предоставляет отличный API, надежный, стабильный и зрелый, и не страдает ни одним из недостатков IPC.
Моя рекомендация для тех, кто придет после меня, такова:
- Если вам просто нужно выполнить другую программу и дождаться ее завершения или собрать ее выходные данные после завершения ее работы, используйте IPC::System::Simple. В противном случае, если вам нужно интерактивно взаимодействовать с чем-то еще, по возможности используйте API. А если это невозможно, то используйте что-то вроде IPC::Run и попытайтесь сделать так, чтобы лучшее из этого — и будьте готовы потратить немало времени, чтобы сделать это «как раз правильно».
person
theory
schedule
22.05.2012
system
и обратных галочек. - person theory   schedule 14.05.2012open |-
. - person theory   schedule 14.05.2012select
!!). Так что, хотя IPC::Run дает вам возможность сделать это, это должно быть последним средством. Плюс IPC::Run по сравнению с другими, которые вы упомянули, заключается в том, что он может скрыть от вас каналы. - person ikegami   schedule 14.05.2012<$fh>
или$fh->getline
для перебора строк… - person theory   schedule 15.05.2012<$fh>
aka$fh->getline
, если у вас более одной трубы. Это может привести к тупику. Выполнение IPC с файловыми дескрипторами действительно сложно, если у вас более одного файлового дескриптора (select
!!). - person ikegami   schedule 15.05.2012