Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Системное программирование > Связь программ через pipe


Автор: Norrator 19.12.2005, 15:51
Привет всем!
Существует задача чтения всего вывода (STDOUT) скрипта1 через pipe скриптом2, при этом скрипт1 позволяет пользователю вводить данные с клавиатуры. Скрипт1 работает, но как только выполнить ./скрипт1 | ./скрипт2 появляются проблемы, а именно, сам ввод предшествует приглашению на ввод. В чём проблема?
Усложнение: Скрипт1 править нельзя!!!!

Код

#Скрипт1

open(STDERR ,">&STOUT");
select(STDOUT);
$| = 1;
print STDOUT "HOSTNAME1  :";
$hn = <STDIN>;
.......
close(STDERR);


Код

#Скрипт2

while($str = <>)
{
  print STDOUT "piped_$str";
} 

Автор: sharq 19.12.2005, 16:42
Norrator
Цитата(Norrator @ 19.12.2005, 16:51)
сам ввод предшествует приглашению на ввод. В чём проблема?

Проблема в том, что это не ввод первого скрипта предшествует приглашению,
а ввод второго скрипта:
Цитата(Norrator @ 19.12.2005, 16:51)
$str = <>


Вот полезно будет, работа через pipe.
Цитата

Двустороннее взаимодействие с другим процессом

Если так удобно реализовано однонаправленное взаимодействие, что у нас с двусторонним взаимодействием? Такой очевидный прием на практике не работет:

    open(PROG_FOR_READING_AND_WRITING, "| какая-то программа |")

и если вы забудете указать use warnings или флаг -w, то вы пропустите диагностическое сообщение:

    Can't do bidirectional pipe at -e line 1.
    Невозможно создать двунаправленный канал

Если вам действительно необходим двунаправленный канал, вы можете использовать стандартную библиотечную функцию open2(), чтобы перехватывать ввод и вывод программы. Существует также функция open3() для трехнаправленного ввода/вывода, таким образом вы можете перехватывать STDERR дочернего процесса, но использование этой функции требует последующего неуклюжего цикла select() и не позволяет пользоваться обычными операциями ввода Perl.

Если вы посмотрите исходный код, вы увидите, что open2() использует низкоуровневые примитивы, такие как Unix вызовы pipe() и exec(), для создания всех соединений. Хотя использование этой функции может быть несколько более эффективным, чем socketpair(), она не является такой уж переносимой. Маловероятно, что функции open2() и open3() будут работать где-либо, кроме Unix систем или систем, претендующих на совместимость с POSIX.

Вот пример использования open2():

    use FileHandle;
    use IPC::Open2;
    $pid = open2(*Reader, *Writer, "cat -u -n" );
    print Writer "stuff\n";
    $got = <Reader>;

Здесь имеется проблема, буферизация Unix действительно может испортить вам день. Даже если ваш дескриптор Writer является auto-flushed дескриптором, и процесс на другом конце получает свои данные своевременно, обычно у вас нет никаких средств заставить его возвращать данные также своевременно. Поэтому мы указали cat флаг -u, чтобы сделать вывод небуферизованным. Однако очень мало команд Unix разработано для работы с каналами (pipes), поэтому такой код редко срабатывает, только если вы сами не напишите программу, которая будет работать на другом конце двунаправленного канала.

Решением является использование нестандартной библиотеки Comm.pl. Она использует pseudo-ttys, чтобы сделать поведение вашей программы более корректным:

    require 'Comm.pl';
    $ph = open_proc('cat -n');
    for (1..10) {
        print $ph "строка\n";
        print "получили ответ ", scalar <$ph>;
    }

Таким образом, у вас нет необходимости в доступе к исходному коду используемой программы. Библиотека Comm также содержит функции expect() и interact(). Вы можете найти библиотеку (и, надеемся, ее преемника IPC::Chat) в ближайтем архиве CPAN.

Более новый модуль Expect.pm из CPAN также предназначен для подобных вещей. Этот модуль требует для работы два других модуля из CPAN: IO::Pty и IO::Stty. Он настраивает псевдо-терминалы для взаимодействия с программами, которые работают с драйвером терминального устройства. Если в вашей системе такое поддерживается - этот модуль будет лучшим выбором.



smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)