Вопрос по ipc, pipe, filehandle, perl – Какой предпочитаемый кроссплатформенный модуль IPC Perl?

15

Я хочу создать простой объект ввода-вывода, который представляет канал, открытый для другой программы, который я могу периодически записывать в STDIN другой программы во время работы моего приложения. Я хочу, чтобы он был пуленепробиваемым (в том смысле, что он ловит все ошибки) и кроссплатформенным. Лучшие варианты, которые я могу найти:

open
<code>sub io_read {
    local $SIG{__WARN__} = sub { }; # Silence warning.
    open my $pipe, '|-', @_ or die "Cannot exec $_[0]: $!\n";
    return $pipe;
}
</code>

Преимущества:

Cross-platform Simple

Недостатки

No $SIG{PIPE} to catch errors from the piped program Are other errors caught? IO::Pipe
<code>sub io_read {
    IO::Pipe->reader(@_);
}
</code>

Преимущества:

Simple Returns an IO::Handle object for OO interface Supported by the Perl core.

Недостатки

Still No $SIG{PIPE} to catch errors from the piped program Not supported on Win32 (or, at least, its tests are skipped) IPC::Run

В IPC :: Run нет интерфейса для записи в дескриптор файла, только добавление в скаляр. Это кажется странным.

IPC::Run3

Здесь нет интерфейса дескриптора файла. Я мог бы использовать ссылку на код, которая будет вызываться повторно для спулинга дочернему элементу, но, глядя на исходный код, кажется, что он фактически записывает во временный файл, а затем открывает его и спулингируетits содержимое в трубу 'd команды'STDIN, Wha?

IPC::Cmd

Все еще нет интерфейса дескриптора файла.

Что мне здесь не хватает? Кажется, что это должно быть решенной проблемой, и я немного ошеломлен, что это не так. IO :: Труба ближе всего подходит к тому, что я хочу, но отсутствие$SIG{PIPE} Обработка ошибок и отсутствие поддержки Windows беспокоит. Где находится модуль трубопровода, который будет JDWIM?

использованиеsigtrap для сигналов трубы. daxim
Вы ошибаетесь по поводу IPC :: Run. Он может обрабатывать файлы без проблем. Он может даже делать сумасшедшие перенаправления и псевдо-тты. ikegami
Повторяю, что мне нужен дескриптор файла, который я могу ЗАПИСАТЬ: так что используйте канал. Как ты и сказал, что хочешь сделать. Есть даже встроенная поддержка для создания этих каналов для вас. Это прямо здесь, в кратком обзоре !!! ikegami
@ Теория, нет, вы не можете использовать<$fh> ака$fh->getline безопасно, если у вас есть более одной трубы. Это может привести к тупику. Выполнение IPC с файловыми дескрипторами действительно сложно, если у вас есть несколько файловых дескрипторов (select!!). ikegami
Обратите внимание, что выполнение IPC с файловыми дескрипторамиreally трудно, если у вас есть более одного дескриптора файла (select!!). Так что, хотя IPC :: Run дает вам возможность сделать это, это должно быть последним средством. Плюс IPC :: Запуск над другими, о которых вы упомянули, заключается в том, что он может скрыть от вас каналы. ikegami

Ваш Ответ

2   ответа
0

Я сделал что-то похожее на это. Хотя это зависит от родительской программы и того, что вы пытаетесь передать. Мое решение состояло в том, чтобы порождать дочерний процесс (оставив $ SIG {PIPE} для работы) и записывать это в журнал, или обрабатывать ошибку, как вам удобно. Я использую POSIX для управления своим дочерним процессом и могу использовать все функции родительского процесса. Однако, если вы пытаетесь заставить ребенка общаться с родителем, тогда все становится сложнее. У вас есть пример основной программы и что вы пытаетесь ТРУБИТЬ?

Главным образом я хочу знать, выходит ли ребенок, и распространил ли эту смерть на родителя, чтобы затем я мог справиться с этой ситуацией. Основная программаSqitchи дочерние приложения будутpsql, mysql, sqlite3, и тому подобное. theory
5

Благодаря руководству @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 собирать выходные данные в массив, а не в скаляр (просто передайте ссылку на скаляр'>' если ты этого хочешь).

Так что это работает, но, по моему мнению, отстой по целому ряду причин:

  • There is no generally useful way to know that an IPC-controlled program is done printing to its STDOUT. Instead, you have to use a regular expression on its output to search for a string that usually means it's done.
  • If it doesn't emit one, you have to trick it into emitting one (as I have done here—god forbid if I should have a file named __END__, though). If I was controlling a database client, I might have to send something like SELECT 'IM OUTTA HERE';. Different applications would require different new_appender hacks.
  • The writing to the magic $in and $out scalars feels weird and action-at-a-distance-y. I dislike it.
  • One cannot do line-oriented processing on the scalars as one could if they were file handles. They are therefore less efficient.
  • The ability to use new_chunker to get line-oriented output is nice, if still a bit weird. That regains a bit of the efficiency on reading output from a program, though, assuming it is buffered efficiently by IPC::Run.

Теперь я понимаю, что, хотя интерфейс для IPC :: Run может быть немного более приятным, в целом, в частности, из-за недостатков модели IPC сложно вообще разобраться. Не существует общепринятого интерфейса IPC, потому что нужно знать слишком много о специфике конкретной программы, которая запускается, чтобы заставить ее работать. Это нормально,maybeЕсли вы точно знаете, как он будет реагировать на входные данные, и сможете надежно распознать, когда это будет сделано, испуская выходные данные, и вам не нужно будет сильно беспокоиться о кроссплатформенной совместимости. Но этого было далеко не достаточно для моей потребности в общепринятом способе взаимодействия с различными клиентами командной строки базы данных в модуле CPAN, который можно было бы распространить на целый хост операционных систем.

В конце концов, благодаря упаковке предложений в комментарияхсообщение в блогеЯ решил отказаться от использования IPC для управления этими клиентами и использоватьDBIвместо. Он обеспечивает отличный API, надежный, стабильный и зрелый, и не имеет ни одного из недостатков IPC.

Моя рекомендация для тех, кто придет за мной:

  • If you just need to execute another program and wait for it to finish, or collect its output when it is done running, use IPC::System::Simple. Otherwise, if what you need to do is to interactively interface with something else, use an API whenever possible. And if it's not possible, then use something like IPC::Run and try to make the best of it—and be prepared to give up quite a bit of your time to get it "just right."

Похожие вопросы