Expect(3)

Материал из wiki.lissyara.su
Перейти к: навигация, поиск

НАЗВАНИЕ

Expect.pm - Expect для Perl

ВЕРСИЯ

1.21

СИНТАКСИС

use Expect;
# создание Expect объекта с помощью порождения другого процесса
my $exp = Expect->spawn($command, @params)
 or die "Cannot spawn $command: $!\n";
# или с помощью уже созданного и открытого дескриптора файла (например из Net::Telnet)
my $exp = Expect->exp_init(\*FILEHANDLE);
# или если Вы предпочитаете ОО настройки:
my $exp = new Expect;
$exp->raw_pty(1);
$exp->spawn($command, @parameters)
 or die "Cannot spawn $command: $!\n";
# отправка некоторой строки:
$exp->send("string\n");
# или, вариант для дескриптора:
print $exp "string\n";
# затем сравнивание некоторых образцов с любым простым интерфейсом
$patidx = $exp->expect($timeout, @match_patterns);
# или мульти-сравнение нескольких порождённых комманд с обратным вызовом,
# только для Tcl версии
$exp->expect($timeout,
             [ qr/regex1/ => sub { my $exp = shift;
                                   $exp->send("response\n");
                                   exp_continue; } ],
             [ "regexp2" , \&callback, @cbparms ],
            );
# если больше не нужно, выполняем soft_close для аккуратного завершения команды
$exp->soft_close();
# или при потере пациента с помощью
$exp->hard_close();
Expect.pm создаёт в любом порождении процесс или берёт уже созданный дескриптор и взаимодействует с ним таким образом, что интерактивные задачи могут выполняться без вмешательства оператора. Эта понятие подразумевает много значений, если Вы уже работали с разными Tcl версиями Expect'а. Функции предоставляемые с помощью Expect.pm:
Expect->new()
Expect::interconnect(@objects_to_be_read_from)
Expect::test_handles($timeout, @objects_to_test)
Expect::version($version_requested | undef);
$object->spawn(@command)
$object->clear_accum()
$object->set_accum($value)
$object->debug($debug_level)
$object->exp_internal(0 | 1)
$object->notransfer(0 | 1)
$object->raw_pty(0 | 1)
$object->stty(@stty_modes) # See the IO::Stty docs
$object->slave()
$object->before();
$object->match();
$object->after();
$object->matchlist();
$object->match_number();
$object->error();
$object->command();
$object->exitstatus();
$object->pty_handle();
$object->do_soft_close();
$object->restart_timeout_upon_receive(0 | 1);
$object->interact($other_object, $escape_sequence)
$object->log_group(0 | 1 | undef)
$object->log_user(0 | 1 | undef)
$object->log_file("filename" | $filehandle | \&coderef | undef)
$object->manual_stty(0 | 1 | undef)
$object->match_max($max_buffersize or undef)
$object->pid();
$object->send_slow($delay, @strings_to_send)
$object->set_group(@listen_group_objects | undef)
$object->set_seq($sequence,\&function,\@parameters);
Есть несколько конфигурируемых переменных, которые влияют на поведение Expect'а. Вот они:
$Expect::Debug;
$Expect::Exp_Internal;
$Expect::IgnoreEintr;
$Expect::Log_Group;
$Expect::Log_Stdout;
$Expect::Manual_Stty;
$Expect::Multiline_Matching;
$Expect::Do_Soft_Close;

ОПИСАНИЕ

Модуль Expect преёмник Comm.pl и потомок Chat.pl. Также есть много сходств с языком Tcl, который является его предшественником. Expect не содержит кода из Comm.pl для работы с сетью. Я думаю, что упоминание о IO::Socket и внешних инструментах, таких как netcat, будет не уместно.
Expect.pm написан с расчётом охватить как можно больше switch()'ев и case'ов, для реализации более гибкого принятия решений. Три режима отладки облегчают написание кода.
Также можно связывать вместе несколько дескрипторов файлов (и процессов), подобно Expect'у в языке Tcl. Это попытка включить все возможности Expect'а в Tcl'е без применения самого Tcl. :-).
Пожалуйста, прежде чем Вы решить использовать Expect, прочитайте ЧАВО на тему "Я хочу автоматизировать ввод пароля для su/ssh/scp/rsh/..." и "Я хочу использовать Expect для автоматизации [подставьте_то_что_хотите]..."

ИСПОЛЬЗОВАНИЕ

new Expect ()
Создать новый объект Expect'а, т.е. pty. Вы можете изменять параметры до порождения команды. Это важно, если вы хотите модифицировать настройки терминала для подчинённого (slave) процесса. Смотрите slave() ниже. Объект возвращает дескриптор IO:pty, имеются дополнительные методы.
Expect->exp_init(\*FILEHANDLE) или
Expect->init(\*FILEHANDLE)
Инициализация $new_handle_object для использования с другими Expect функциями. Должно проходить _ссылкой_ к ДЕСКРИПТОРУ если Вы хотите правильной работы. Предпочтительней IO::File объекты. Возвращает ссылку на новый созданный объект.
Можно использовать только настоящие дескрипторы, явно связанные с дескрипторами (например Net::SSH2), правильная работа при отсутствии fileno() невозможна. Можно использовать объекты Net::Telnet, но, отчёт будет получать только некоторые хосты.
YMMV.
Expect->spawn($команда, @параметры) или
$object->spawn($команда, @параметры) или
new Expect ($команда, @параметры)
Форки и выполнение $команды. В случае удачи возвращает объект Expect'а, в случае ошибки или не найденной команды возвращает "undef". spawn() передаёт без изменений параметры команде exec() языка Perl. Для более подробной информации смотрите семантику команды exec() языка Perl.
Помните, что в случае, когда spawn не может выполнить exec() для заданной команды, то объект Expect'а будет оставаться в силе и следующий expect() получит вывод "Cannot exec" ("Невозможно выполнить"), таким образом объекты можно использовать для определения ошибок.
Также, следует помнить, что Вы не сможете заново использовать объекты, с уже запущенной командой, даже если команда завершила свою работу. Извините, но Вы можете создать новый объект...
$object->debug(0 | 1 | 2 | 3 | не определено)
Устанавливает уровень отладки для $object. 1 обозначает выдачу основной отладочной информации, 2 обозначает выдачу избыточной информации и 0 означает отключение отладки. Если Вы вызвали debug() без параметров, будет возвращён текущий отладочный уровень. При создании объекта уровень отладки соответствует значению $Expect::Debug, обычно это 0.
Значение '3' появилось в версии 1.05 и включает дополнительную функциональность. Это означает, получение _полного_ вывода всех данных прочитанных из объекта Expect'а. Можно осуществить с помощью запроса. Я рекомендую не пользоваться этой функциональностю, за исключением особых обстоятельств.
$object->exp_internal(1 | 0)
Устанавливает/убирает 'exp_internal' отладку. Подобно аналогичной команде языка Tcl. Особо ценно при отладке expect() последовательностей. При создании объекта, значение exp_internal соответствует значению $Expect::Exp_Internal, обычно устанавливается в 0. Возвращает текущую настройку при вызове без параметров. Настоятельно рекомендуется при отладке особо запутанного кода.
$object->raw_pty(1 | 0)
Устанавливает pty в raw режим перед порождением. При этом подавляется эхо, перевод CR->LF и изменяется взаимодействие с TTY операционной системы Solaris (которая посылает <пробел><backspace> приводящая к замедлению работы) и, таким образом, даёт больше возможностей для конвеерной обработки (особенно важно при передаче бинарного содержимого). Помните, что эта опция устанавливается только перед порождением программы!
$object->stty(qw(режим1 режим2...))
Устанавливает режимы tty для $object'ов ассоциированных с терминалом. Помните, что во многих системах главная часть pty не является tty, так что взамен tty Вы можете модифицировать подчинённое pty, смотрите следующий пункт. Для работы необходим установленный IO::Stty, который больше не требуется.
$object->slave()
Возвращает дескриптор подчинённой части псевдо терминала. Очень полезно при модификации терминальных настроек:
$object->slave->stty(qw(raw -echo));
Обычные значения: 'sane', 'raw', и 'raw -echo'. Помните, что Я рекомендую устанавливать настройки терминала в 'raw' или 'raw -echo', поскольку эти настройки позволяют избежать коллизий и дают конвеерное (то есть, прозрачное) управление (без проблем буферизации).
$object->print(@строки) или
$object->send(@строки)
Отправляет данные строки к порождённой команде. Помните, что строки не заносятся в логи (смотрите print_log_file) но, возможно возвращать эхо к псевдоустройству в зависимости от настроек псевдоустройств (по умолчанию установлено в echo). Это должно быть взято в счёт при ожидании (expect()) ответа: следующая строка должна быть посланной командой. Я предлагаю установить настройки псевдо терминала в raw, которое отключает эхо и в результате псевдо терминал становится двунаправленно прозрачным.
$object->expect($таймаут, @образцы совпаденией)
больше похоже на Tcl/Expect,
expect($таймаут,
       '-i', [ $объект11, $объект2, ... ],
             [ $регексп_образец, sub { ...; exp_continue; }, @подпараметры, ],
             [ 'eof', sub { ... } ],
             [ 'timeout', sub { ... }, \$подпараметры1 ],
       '-i', [ $объект_номер, ...],
             '-ex', $точный_образец, sub { ... },
             $точный_образец, sub { ...; exp_continue_timeout; },
             '-re', $регексп_образец, sub { ... },
       '-i', \@список_объектов, @список_образцов,
       ...);
Простой интерфейс:
Задаёт $таймаут Expect'а в секундах, на протяжении которых ожидается совпадение с точным сравнимым_образцом по умолчанию от дескриптора $объекта. Если нужно провести сравнение с использованием регулярных выражение, следует установить префикс '-re'.
По причине некоторых ограничений $таймаут должен быть целым числом. Если $таймаут установлен в 0 Expect единожды проверит совпадение_образцов с выводом. Если $таймаут не определён, то Expect будет вечно ожидать совпадения образца.
При вызове в скалярном контексте, expect() вернёт позицию совпадающего образца в пределах $сравниваемых_образцов, или "undef" при несовпадении образца. Позиция начинается с 1, если Вам понадобится узнать какой из массива @соответствующих_образцов совпал, вычтите единицу из возвращаемого значения
При вызове в контексте массива expect() вернёт ($matched_pattern_position, $error, $successfully_matching_string, $before_match и $after_match).
$matched_pattern_position содержит значение, которое вернётся если expect() вызван в скалярном контексте. $error - ошибка возвращаемая expect()'ом. $error содержит число следующее за строкой эквивалентной описанию ошибки. Если переменные неопределены, это значит отсутствие ошибки, '1:TIMEOUT' означает истечение секунд указанных в $таймауте и не получение совпадающего значения, '2:EOF' обозначает достижение конца строки от $объекта, '3: spawn id($номер_файла) died' обозначает завершение процесса без получения совпадения и '4:$!' обозначает ошибку установленную в $ERRNO в течении последнего чтения с дескриптора $объекта или в течении работы оператора select(). Все дескрипторы обозначаются установкой_группы плюс к тому же, STDOUT получает все данные приходящие от $объекта при работе оператора expect(), если установлены log_group и log_stdout.
Отличия от старых версий - обработка регулярных выражений. Теперь по умолчанию, все строки проходящие через expect() обрабатываются как литералы. Для сравнения с использованием регулярных выражений добавьте '-re' в качестве параметра в начало образца.
Пример:
$object->expect(15, 'match me exactly','-re','match\s+me\s+exactly');
Это изменение сделало возможным сравнивать литералы и регулярные выражения в команде expect().
Ещё одно нововведение: сравнивание нескольких строк. ^ используется для сравнивания начала строки. К сожалению, поскольку perl не использует $/ в определении обрыва строк и $ используется для нахождения конца строки, то эта функция часто не срабатывает. Причина этому - возврат терминалом "\r\n" в конце каждой строки. Один из путей проверки конца строки в регулярных выражениях - это использование \r?$ взамен $.
Пример: Порождение telnet'а к узлу, Вам может понадобиться проверить на наличие символа выхода. telnet может вернуть Вам "\r\nEscape character is '^]'.\r\n". Для нахождения этого можно использовать следующее $match='^Escape char.*\.\r?$';
$telnet->expect(10,'-re',$match);
Новое от Tcl/Expect-подобного интерфейса:
Теперь возможно ожидать более чем одно соединение одновременно с помощью '"-i"' и единственный Expect объект преобразуется в массив содержащий Expect объекты, т.е.
expect($таймаут,
       '-i', $exp1, @образцы_1,
       '-i', [ $exp2, $exp3 ], @образцы_2_3,
      )
Кроме того, образцы могут теперь определяться преобразованным массивом содержащим [$regexp, sub { ...}, @optional_subprams]. При соответствии образцу, вызывается подпрограмма с параметрами ($совпадающий_ожидаемый_объект, @опциональные_подпараметры). Подпрограмма может вернуть символ `exp_continue' для продолжения ожидания сравнивания с изначально заданным таймаутом или вернуть символ `exp_continue_timeout' для продолжения ожидания без сброса счётчика таймаута.
$exp->expect($timeout,
             [ qr/username: /i, sub { my $self = shift;
                                      $self->send("$username\n");
                                      exp_continue; }],
             [ qr/password: /i, sub { my $self = shift;
                                      $self->send("$password\n");
                                      exp_continue; }],
             $shell_prompt);
`expect' в данном случае экспортирован по умолчанию.
$object->exp_before() или
$object->before()
before() возращает 'предыдущую' часть последнего вызова expect(). Если последний expect() вызов не может ничего сравнить, exp_before() вернёт накопленный целый вывод объекта перед завершением вызова expect().
Помните что эта функция несколько отличается от Expect'овского before() языка Tcl!!
$object->exp_after() или
$object->after()
возвращает 'последующую' часть последнего вызова expect(). Если последний вызов expect() не смог ничего сравнить, exp_after() вернёт undef().
$object->exp_match() или
$object->match()
возвращает строку соответствующую последнему вызову expect(), undef если нет совпавшей строки.
$object->exp_match_number() или
$object->match_number()
exp_match_number() возвращает число образца соответствующего последнему вызову expect(). Помните, что первый образец в списке образцов равен 1, а не 0. Возвращает undef если нет совпавшего образца.
$object->exp_matchlist() или
$object->matchlist()
exp_matchlist() возвращает список совпавших подстрок от скобок () внутри последнего совпавшего регулярного выражения. ($object->matchlist)[0] соответствует $1, ($object->matchlist)[1] соответствует $2 и т.д.
$object->exp_error() или
$object->error()
exp_error() возвращает ошибку созданную последним вызовом expect() при несовпадении образца. Обычно используетя для проверки значения возвращённого оператором before() для обнаружения не соответствующего ни одному образцу детерминированного вывода от объекта.
$object->clear_accum()
Очищает содержимое аккумулятора для $object. Освобождает всё остаточное содержимое операции после expect() или send_slow() так, что следующий вызов expect() видит только новые данные от $object'а. При этом возвращается содержимое аккумулятора.
$object->set_accum($value)
Устанавливает содержимое аккумулятора для $object в $value. При этом возвращается предыдущее содержимое аккумулятора.
$object->exp_command() или
$object->command()
exp_command() возвращает строку использованную в порождающей строке. Полезно для отладки и для заново используемых подпрограмм сравнивания образца.
$object->exp_exitstatus() или
$object->exitstatus()
Возвращает статус выхода $object'а (если он уже создан).
$object->exp_pty_handle() или
$object->pty_handle()
Возвращает строку представляющую присоединённый псевдо терминал, на пример: `spawn id(5)' (псевдо терминал имеет файловый номер 5), `handle id(7)' (псевдо терминал инициализирован от файлового номера 7) или `STDIN'. Полезно при отладке.
$object->restart_timeout_upon_receive(0 | 1)
При установке в 1, таймаут expect'а сбрасывается при любом получении данных от порождённого процесса. Применяется для выполнения проверки существования процесса и дальнейшего ожидания данных для сравнивания.
$exp->restart_timeout_upon_receive(1);
$exp->expect($timeout,
             [ timeout => \&report_timeout ],
             [ qr/pattern/ => \&handle_pattern],
            );
Теперь таймаут не переключается если команда производит любой произвольный вывод, т.е. работает, но Вы можете продолжать работать со сравнением образцов от вывода.
$object->notransfer(1 | 0)
Не обрезать содержимое аккумулятора после сравнивания. Обычно, аккумулятор установлен на хранение остатка после сравнивания строк. Помните, что эта настройка происходит через объект, так что, если Вы хотите получить нормальную реакцию (обрезание данных в аккумуляторе) на образцы, добавьте
$exp->set_accum($exp->after);
в обратный вызов, т.е.
$exp->notransfer(1);
$exp->expect($timeout,
             # аккумулятор не обрезается, pattern1 сравнивается снова
             [ "pattern1" => sub { my $self = shift;
                                   ...
                                 } ],
             # аккумулятор обрезается, pattern2 повторно не сравнивается
             [ "pattern2" => sub { my $self = shift;
                                   ...
                                   self->set_accum($self->after());
                                 } ],
            );
Это временный выход из ситуации, пока Я не перепишу часть кода отвечающего за сравнение образцов таким образом, чтобы он (код) принимал дополнительный аргумент -notransfer.
Expect::interconnect(@объекты);
Читает от @объектов и печатает в соответствующую @слушающие_группы, пока последовательность выхода не совпадёт с одними из @объектов и ассоциированная функция вернёт 0 или undef. Специальная последовательность выхода 'EOF' возникает в случае, когда дескриптор объекта возвращает конец файла. Помните, что не обязательно включать объекты, которые только принимают данные в @объектах после последовательности выхода _чтения_ от объекта. Также помните, что слушающая_группа для только_пишущих объектов всегда будут пустыми. Зачем Вам может понадобиться иметь слушающие объекты в STDOUT (на пример)? По умолчанию, каждый из @объектов _также относится к его слушающей группе_ устанавливается в 'raw -echo' для дальнейшей взаимосвязи. Настройка $объект->manual_stty() остановит это действие для объекта. Изначальные настройки терминала восстанавливаются при завершении связи.
Про общий путь о процессах связи, смотрите IPC::Run.
Expect::test_handles(@объекты)
Данная настройка объектов определяет какие дескрипторы объектов имеют данные доступные для чтения. Возвращает массив членов позиций в @объектах имеющих готовые дескрипторы. Возвращает undef если нет готовых дескрипторов.
Expect::version($запрашиваемая_версия или undef);
Возвращает текущую версию Expect'а. Версии ранее .99 не поддерживаются. Слишком много вещей изменилось в процессе разработки.
$object->interact( "\*FILEHANDLE, $последовательность_выхода")
interact() по существу макрос для вызова interconnect() для соединения 2 процессов вместе. \*FILEHANDLE по умолчанию установлен в \*STDIN и $последовательность_выхода, по умолчанию, установлена в undef. Взаимодействия прекращаются, во время прочтения $последовательности_выхода от FILEHANDLE, а не от $объекта. Слушающая группа $объектов состоит исключительно от \*FILEHANDLE для поддержания взаимодействия. Не производится эхо от \*FILEHANDLE'а в STDOUT.
$object->log_group(0 | 1 | undef)
Включает/отключает ведение логов об $объекте для его 'слушающей группы'. Если установить все объекты в слушающие группы, то можно получить вывод от $объекта выводимого во время $объект->expect(), $объект->send_slow() и "Expect::interconnect($объект , ...)". По умолчанию установлено в значение "включено". Во время создания $объекта установки соответствуют значению $Expect::Log_Group, обычно 1.
$object->log_user(0 | 1 | undef) или
$object->log_stdout(0 | 1 | undef)
Включает/выключает ведение логов о дескрипторе объекта в STDOUT. Соответствует значению log_user языка Tcl. Возвращает текущие значения при вызове без параметров. По умолчанию установлено в off для инициализированных дескрипторов. При создании объекта процесса (не дескриптор инициализированный с помощью exp_init), значение log_stdout соответствует переменной $Expect::Log_Stdout, обычно 1. Если/когда Вы инициализируете STDIN, он обычно ассоциируется с терминалом, который по умолчанию выдаёт эхо в STDOUT, будьте осторожно иначе Вы можете получить множественное эхо.
$object->log_file("имя_файла" | $filehandle | \&coderef | undef)
Запись лог сессии в файл. Все символы отправляемые или принимаемые от порождённого процесса записываются в этот файл. Обычно добавляется в лог файл, но Вы можете применть дополнительный режим "w" для обрезания файла через open():
$object->log_file("имя_файла", "w");
Возвращает дескриптор файла.
Если вызвано с неопределённым (undef) значением, останавливает ведение лога и закрывает лог файл:
$object->log_file(undef);
Если вызвано без аргументов, возвращает дескриптор лог файла:
$fh = $object->log_file();
Может быть установлена ссылка на код, который может быть вызван взамен вывода в лог файл:
$object->log_file(\&myloggerfunc);
$object->print_log_file(@строки)
Печатает в лог файл (если он открыт) или вызывает функцию захвата лог файла. Используется для добавления какого-либо текста в лог файл. Помните, что такой же результат может быть достигнут с помощью $объект->log_file->print(), но будет работать только для лог файлов, а не для кода захвата.
$объекта->set_seq($последовательность, \&функция, \@параметры_функции)
Во время исполнения Expect->interconnect() и при условии что $последовательность прочтено с $объекта, &функция будет выполнена с параметрами @параметры_функции. _Чрезвычайно рекомендуется_ использовать последовательность выхода состоящую из одного символа, поскольку есть очень большая вероятность, что последовательность будет нарушена при разделении чтения от дескриптора $объекта, что в свою очередь может отделить $последовательность от получения вывода слушаемой группы $объекта. \&функция может быть чем-нибудь наподобие 'main::control_w_function' и @параметры_функции могут быть в массиве определённом вызывающим кодом и могут быть переданы ссылкой set_seq(). Ваша функция возвращает не-нулевое значение если выполнение interconnect() возобновляет последующий вывод функции, ноль или не определено (undefined) если interconnect() вернётся после вывода функции. Специальная последовательность 'EOF' обозначает конец файла достигнутый $объектом. Смотрите interconnect() для более детальной информации.
$объект->set_group(@слушающие_объекты)
@слушающие_объекты - список объектов, имеющих дескрипторы в которые производится печать $объектом при вызове Expect::interconnect, $объект->expect() или $объект->send_slow(). Вызов без параметров будет возвращать текущий список слушающих объектов.
$объект->manual_stty(0 | 1 | undef)
Включает/выключает авто настройки терминала для $объекта. Соответствует значению $Expect::Manual_Stty (обычно 0), при условии что $объект создан. При вызове без параметров возвращает текущие manual_stty() настройки.
$объект->match_max($maximum_buffer_length | undef) или
$объект->max_accum($maximum_buffer_length | undef)
Устанавливает максимальный аккумуляторный размер для объекта. Полезно при слишком сильном росте аккумулятора во время вызова expect()'а. Так как, буфер соответствует каждому соответствующему_образцу, то может произойти чрезмерно большой рост, что в свою очередь приводит к снижению производительности. Возвращает текущее значение при вызове без параметров. По умолчанию, не определено.
$объект->notransfer(0 | 1)
Если установлено, соответствующие строки не удаляются с аккумулятора. Возвращает текущее значение при вызове без параметров. Ложь (false) по умолчанию.
$объект->exp_pid() или
$объект->pid()
Возвращает pid $объекта, если он создан. Инициализированные дескрипторы файла не имеют pid (конечно).
$объект->send_slow($задержка, @строки);
печатает каждый символ каждой строки в @строки через заданную $задержку после каждого символа. Удобно для таких устройств как модемы, поскольку они не могут принимать быстро набираемые данные. После каждого символа $объект проверяет готовы или нет новые данные, также обновляется аккумулятор для будущих вызовов expect() и печатается вывод на STDOUT и @слушающую_группу если log_stdout и log_group установлено надлежащим образом.
Конфигурируемые Переменные Пакета:


$Expect::Debug По умолчанию 0. Заново созданные объекты устанавливаются в $объект->debug() от $Expect::Debug. Смотрите $объект->debug();

$Expect::Do_Soft_Close По умолчанию 0. При разрушении объектов, soft_close может занять до половины минуты для завершения всех процессов. Кроме этого, может быть также вызван hard_close являющийся менее правильным путём завершения работы порождённых процессов. Установите в '1' для прежнего поведения.

$Expect::Exp_Internal По умолчанию 0. Новые созданные объекты имеют значение $объект->exp_internal() из $Expect::Exp_Internal. Смотрите $объект->exp_internal().

$Expect::IgnoreEintr По умолчанию 0. При установке в 1 и ожидании новых данных, Expect игнорирует EINTR ошибки и перезапускает вызов select().

$Expect::Log_Group По умолчанию 1. Новым созданным объектам присваивается значение $объект->log_group() из $Expect::Log_Group. Смотрите $объект->log_group().

$Expect::Log_Stdout По умолчанию, 1 для порождённых команд, 0 для дескрипторов файла присоединённых к exp_init(). Новым созданным объектам присваивается значение $объект->log_stdout() из $Expect::Log_Stdout. Смотрите $объект->log_stdout().

$Expect::Manual_Stty По умолчанию 0. Новым созданным объектам присваивается значение $объект->manual_stty() из $Expect::Manual_Stty. Смотрите $объект->manual_stty().

$Expect::Multiline_Matching

По умолчанию 1. Определяет будет ли влиять использование флага /m в команде expect() на сравнивание регулярных выражений. Если установлено в 1 - используется /m.
Эта переменная создаёт различие при сравнивании ^ и $. Если Вы используете такой способ, то сравнивайте строки в середине страницы с использованием ^ и $ взамен сравнения начала и конца всего выражения. Я думаю что такой метод удобен.

ВКЛАД

Ли Икин (Lee Eakin <leakin@japh.itg.ti.com>) портировал сценарий kibitz'а с Tcl/Expect на Perl/Expect.
Джефф Карр (Jeff Carr <jcarr@linuxmachines.com>) предоставил простой пример того, как управлять событиями изменения размера окна терминала (переданных через WINCH сигнал) в ssh сессии.
Вы можете найти оба сценария в поддиректории examples/. Спасибо им обоим!
Исторические заметки:
Есть несколько строк относящихся к датированию кода вдохновившего на написание Comm.pl и Chat.pl модулей без которых не было бы Expect реализации на Perl'е. Слава Эрику Арнольду (Eric Arnold <Eric.Arnold@Sun.com>) и Рэндалу Шварцу (Randal 'Nuke your NT box with one line of perl code' Schwartz<merlyn@stonehenge.com>) за предоставление этого кода публике perl.
Как и в .98 Я думаю все эти старые коды хороши. Без них не было бы возможности сделать всё что уже сделано. Отдельное спасибо Грэхему Барру (Graham Barr) за помощь в освоении IO::Handle и очень полезного IO::Tty модуля.

ССЫЛКИ

Марк Рогаски (Mark Rogaski <rogaski@att.com>) написал:
"Я думаю Вы знаете что Expect.pm очень полезен в Лаборатории AT&T спустя пару лет (с тех пор, как Я поговорил с Остином о дизайне решений). Мы используем Expect.pm для управления свитчами в нашей сети через telnet интерфейс, и такая автоматизация значительно увеличила нашу устойчивость. Таким образом Вы можете с чистым сердцем сказать что одна из величайших цифровых сетей из ныне существующих (AT&T Frame Relay) очень активно использует Expect.pm."

ЧаВО - Часто задаваемые Вопросы и Ответы

Это постоянно пополняющаяся коллекция подсказок, которые могут Вам помочь. Пожалуйста, отправляйте Ваш вопрос который не имеет ответа по адресу RGiersig@cpan.org
На каких системах запускается Expect?
Expect сам по себе не имеет системных зависимостей, но лежащий в его основе IO::Tty требует псевдотерминалов. IO::Stty использует POSIX.pm и Fcntl.pm.
Я использую его на Solaris, Linux и AIX, остальные сообщают, что работает в *BSD и OSF. В целом, современные POSIX Unix'ы поддерживают его, но, есть исключения из каждого правила. Приветствуется обратная связь.
Смотрите IO::Tty для списка проверенных систем.
Могу ли Я использовать этот модуль с ActivePerl'ом на Windows?
На данный момент, ответ 'Нет', но ситуация меняется.
Вы не можете использовать ActivePerl, но если Вы используете окружение Cygwin (http://sources.redhat.com), который содержит свой perl, и имеет установленный последний IO::Tty (версия 0.05 или позднее), то возможна работа Expect'а (обратная связь приветствуется).
Пример в этом руководстве не работает!
Руководство безнадёжно устарело и нуждается в серьёзном пересмотре. Я приношу свои извинения, Я концентрирую свои усилия на функциональности. Приглашаются волонтёры.
Как Я могу узнать что делает Expect?
Если Вы установите
$Expect::Exp_Internal = 1;
Expect очень подробно расскажет Вам что он принимает и что отправляет, что сравнивается и что совпадает с образцами. Вы можете также делать это по командно с
$exp->exp_internal(1);
Вы можете также установить
$Expect::Debug = 1;  # или 2, 3 для более подробного вывода
или
$exp->debug(1);
что выдаст Вам каждый произошедший вывод.
Я вижу вывод порождённой команды. Могу Я отключить это?
Да, достаточно установить
$Expect::Log_Stdout = 0;
для полного отключения или
$exp->log_stdout(0);
только для этой команды. 'log_user' доступен как алиас, так что Tcl/Expect пользователь получает DWIM опыт :-)
Нет, При отправке некоторого текста Я получаю дополнительное эхо на экран и следующий expect реагирует на него.
Причиной этому псевдо терминал, в котором возможно включена 'эхо'. Решение: установить псевдо терминал в raw режим, который в основном чист для соединения между двумя программами (больше нет неожиданных символьных трансляций). К сожалению это может привести к не срабатыванию старого кода, который отправляет программам "\r" взамен "\n" (трансляция также управляется псевдо терминалом), Я не могу добавить это в Expect. Но можно поэкспериментировать с "$exp->raw_pty(1)".
Как Я могу отправить контролирующие символы к процессу?
О: Вы можете отправить любой символ к процессу через команду print. Для отображения контролирующего символа в Perl, используйте \c и следующую за ним букву. На пример, control-G будет отображаться через "\cG". Помните, что этот метод не будет работать если Вы закроете единичными кавычками эту строку. Таким образом, отправка control-C на процесс в $exp, сделайте следующее:
print $exp "\cC";
Или, если Вы предпочитаете:
$exp->send("\cC");
Способность включать контролирующие символы в строку подобно вышеприведённому примеру, существует в Perl'е, а не в Expect.pm. Изучение Expect.pm без глубокого изучения Perl может быть очень сложным занятием. Мы предлагаем Вам заглянуть в превосходные учебные материалы по Perl'у, например: "Программирование на Perl" и "Изучение Perl" за авторством О'Рэйли, кроме того, обширная он-лайн документация доступна через команду perldoc.
Мой сценарий периодически перестаёт работать без видимых причин. И, кажется, Я периодически теряю вывод от порождённой программы.
Возможно Вы слишком быстро завершаете работу, не давая порождённой программе корректно завершить работу. Попробуйте добавить $exp->soft_close() для аккуратного завершения программы или используйте expect() на 'eof'.
В качестве альтернативы, попробуйте добавить 'sleep 1' после команды spawn(). Также причиной может быть медленное создание псевдо терминала на Вашей системе (хотя это невозможно при использовании последнего IO-Tty).
Я хочу автоматизировать ввод пароля для su/ssh/scp/rsh/...
Не следует использовать Expect для этих целей. Ввод пароля, особенно пароля от root'а, в сценарии в открытом виде является серьёзной брешью в системе безопасности. Я настойчиво рекомендую использовать для этих целей другие способы. Для 'su', рассмотрим переключение в 'sudo', даёт root доступ для каждой команды и для каждого пользователя, что позволяет обойтись без ввода пароля. 'ssh'/'scp' может быть настроен на использование RSA аутентификации без паролей. 'rsh' может использовать механизм .rhost, но Я настойчиво рекомендую перейти на 'ssh'; понятие 'rsh' и 'безопасность' являются оксюмороном.
Этот метод работает для 'telnet', хотя, Вы можете также использовать его и для 'ssh', но помните: хранение паролей в открытом тексте - это очень большая уязвимость.
Я хочу использовать Expect для автоматизации [подставьте любое слово]...
Вы уверены что нет другого, более лёгкого пути? Как правило, Expect предназначен для автоматизации программ работающих с людьми, где нет другой возможности. Для задач следующих чётко определённым протоколам, есть более лучшие модули. Не пробуйте отправлять HTTP запросы через telnet по 80 порту, используйте LWP. Для автоматизации FTP, обратите внимание на Net::FTP или "ncftp" (http://www.ncftp.org). Не забивайте гвозди микроскопом.
Возможно ли использование нитей с Expect'ом?
В общем то да, но с одним ограничением: Вы должны породить (spawn()) Вашу программу в главной нити и затем передать Expect объекты управляющей нити. Причиной этому является spawn() использующий fork(), и perlthrtut:
"Думаете о смешивании fork() и нитей? Пожалуйста, подождите пока эта мысль не покинет Вашу голову."
Я хочу сохранить в лог файл весь сеанс.
Используйте
$exp->log_file("filename");
или
$exp->log_file($filehandle);
или даже
$exp->log_file(\&log_procedure);
для большей гибкости.
Помните, что лог файл по умолчанию дописывается, но Вы можете определить опциональный режим "w" для обрезания лог файла:
$exp->log_file("filename", "w");
Для прекращения ведения лога, достаточно вызвать ту же команду с ложным аргументом:
$exp->log_file(undef);
Как Я могу отключить многострочное сравнивание для моих регулярных выражений?
Для глобального отключения многострочного сравнивания для всех регулярных выражений:
$Expect::Multiline_Matching = 0;
Вы можете также выполнить аналогичную операцию для каждого регулярного выражения установкой "(?-m)" внуть регулярного выражения (для этого нужна версия perl от 5.00503 или позднее).
Как Я могу expect'ить многострочные порождённые команды?
Вы можете использовать параметр -i для описания единственного объекта или списка Expect объектов. Все следующие образцы будут сравнены вопреки списку.
Вы можете определить -i для создания группы объектов и образцов для сравнивания вопреки expect синтаксису.
Это работает также, как и в Tcl/Expect.
Смотрите пример исходника ниже.
Мне кажется, у меня проблемы с псевдотерминалами?
Ну, управление псевдотерминалами это как чёрная магия, так как, сильно зависит от системы. Я очень серьёзно изучал IO-Tty, так что этой проблемы не должно быть.
Если Ваша система находится в "белом" списке модуля IO::Tty, Вы возможно произвели у себя не стандартную настройку, т.е. по особому скомпилировали ядро Linux и отключили псевдо терминалы. Пожалуйста, попросите знакомого сисадмина помочь Вам.
Если Ваша система не находится в списке, распакуйте последнюю версию IO::Tty, выполните следующее 'perl Makefile.PL; make; make test; uname "-a"' и отправьте мне результаты и Я посмотрю что можно будет сделать.
Я хочу только прочитать вывод от процесса без expect()'а. Как Я могу это сделать?
[ Вы уверены что Вам нужен именно Expect для этих целей? Как насчёт qx() или open("prog|")? ]
Используя expect без каких-либо образцов для сравнения.
$process->expect(undef); # Всегда пока не достигнут EOF
$process->expect($timeout); # на несколько секунд
$process->expect(0); # Есть что-нибудь готовое для управления?
Хорошо, итак, как Я могу получить то, что было прочитано при управлении?
$read = $process->before();
Где расположено IO::Pty?
Найдёте его в CPAN как IO-Tty.
Как заставить умирать passwd при попытке изменения пароля периодически/постоянно?
Что произойдёт при закрытии управления перед завершением работы passwd. В то время, когда Вы закрывате управление процессом, он (процесс) посылает сигнал (SIGPIPE?) говорящий что STDOUT больше не существует. По умолчанию, поведение для процесса при этом обстоятельстве - умирать. Два пути предотвратить это:
$process->soft_close();
При этом ожидается 15 секунд для достижения процессом EOF перед его завершением.
$process->expect(undef);
Вечное ожидание соответствия процесса пустому образцу. Возврат произойдёт при достижении процессом EOF.
Как правило, Вы должны всегда ожидать результата Вашей транзакции перед продолжением дальнейшей работы.
Что происходит когда Я пробую получить лог файл с помощью log_file() или set_group() и не печатается ничего после последнего запуска expect()?
Вывод только печатается в лог файл/группу при чтении Expect'ом от процесса, на протяжении работы expect(), send_slow() и interconnect(). Один путь форсирования этого, использование
$process->expect(undef);
и
$process->expect(0);
что запустит expect() с пустым образцом для захвата вывода от $process. Вывод доступен в аккумуляторе, так Вы можете получать данные используя $process->before().
У меня проблемы с настройками терминала, двойное эхо и т.д.
Настройки терминала главная проблема для отслеживания. Если Вы обнаружили неожиданное поведение, такое как двойное эхо или замороженную сессию, прочитайте документацию о настройках по умолчанию. Если у Вас всё ещё есть сомнения, попробуйте управлять с помощью функций $exp->stty() и manual_stty(). Как .98 Вас не должны волновать настройки терминала, если только Вы сами не измените их (как например, выполнение -echo для получения пароля).
Если Вы сбили настройки Вашего терминала, убейте все процессы работающие с этим терминалом и введите 'stty sane' в приглашение оболочки. Этот способ вернёт Вам контроль над терминалом.
Помните, что IO::Tty возвращает псевдо терминалы с Вашими терминальными настройками по умолчанию, по отношению к эху, CRLF трансляциям, т.д. и Expect не изменит их. Я продумал настройки для псевдо терминалов для установки в наиболее оптимальный режим без любых трансляций, но они (настройки) могут сбить некоторые существующие вещи, например, '\r' трансляция не будет работать. С другой стороны, raw псевдо терминалы больше подходят для работы в конвеерном стиле и более соответствуют WYGIWYE (what you get is what you expect - что получил то и ожидаешь), Вы можете установить в 'raw' следующим образом:
$exp = new Expect;
$exp->raw_pty(1);
$exp->spawn(...);
Для отключения эхо:
$exp->slave->stty(qw(-echo));
Я породил telnet/ssh сессию и хочу дать возможность взаимодействовать с ним. Но, screen ориентированные приложения с другой стороны не будут работать корректно.
Для этого Вы можете установить размеры экрана терминала. К счастью, IO::Pty имеет такие методы, поэтому, модифицируйте свой код следующим образом:
my $exp = new Expect;
$exp->slave->clone_winsize_from(\*STDIN);
$exp->spawn("telnet somehost);
Также, некоторые приложения требуют установленной переменной оболочки TERM, оттуда они узнают как передвигать курсор через экран. При ведении логов, удалённая оболочка посылает последовательность (Я думаю Ctrl-Z) и ожидает ответа от терминала со строкой, т.е. 'xterm'. Если Вы действительно хотите пойти этим путём (знайте, в конце будет безумие), Вы можете управлять им и отсылать назад значение в $ENV{TERM}. Это объяснения, как говорится, на пальцах, пожалуйста, отредактируйте его под себя.
Я установил размер терминала так, как указано выше, но если Я изменяю окно, приложения не знают об этом.
Вы можете перехватить сигнал WINCH ("window size changed" - "размер окна изменился"), изменить размер терминала и распространить сигнал в порождённые приложения:
my $exp = new Expect;
$exp->slave->clone_winsize_from(\*STDIN);
$exp->spawn("ssh somehost);
$SIG{WINCH} = \&winch;

sub winch {
  $exp->slave->clone_winsize_from(\*STDIN);
  kill WINCH => $exp->pid if $exp->pid;
  $SIG{WINCH} = \&winch;
}

$exp->interact();
Есть пример файла ssh.pl в поддиректории examples/, который показывает как работать с ssh. Пожалуйста, помните что Я против такой автоматизации входа в систему через ssh (смотрите ssh-keygen).
Я заметил, что тест использует строку напоминающую, но не точно соответствующий исходной. Что это значит?
Это значит, что Вы анально сдерживаетесь. :-) [Здесь ты попался!]
Я получил сообщение "Could not assign a pty" ("Невозможно назначить псевдо терминал") при запуске как не-root пользователь на IRIX системе?
Может быть операционная система не сконфигурирована для выделения дополнительных псевдо терминалов для не-root пользователей. /usr/sbin/mkpts должен быть 4755, а не 700 для корректной работы. Я не знаю о последствиях касающихся безопасности.
Почему Я не получаю уведомления при закрытии порождённого процесса на его stdin/out/err??
Возможно Вы работаете на системе, на которой породитель не получает EOF на stdin/out/err при закрытии порождения.
Одно возможное решение при порождении процесса - написать за ним уникальную строку, для определения завершения процесса.
$process = Expect->spawn('telnet somehost; echo ____END____');
And then process->expect($timeout,'____END____','other','patterns');

Примеры Исходников

Как автоматизировать вход в систему
my $telnet = new Net::Telnet ("remotehost") # смотрите Net::Telnet
  or die "Cannot telnet to remotehost: $!\n";;
my $exp = Expect->exp_init($telnet);

# устаревшее использование порождённой telnet команды
# my $exp = Expect->spawn("telnet localhost")
#   or die "Cannot spawn telnet: $!\n";;

my $spawn_ok;
$exp->expect($timeout,
             [
              qr'login: $',
              sub {
                $spawn_ok = 1;
                my $fh = shift;
                $fh->send("$username\n");
                exp_continue;
              }
             ],
             [
              'Password: $',
              sub {
                my $fh = shift;
                print $fh "$password\n";
                exp_continue;
              }
             ],
             [
              eof =>
              sub {
                if ($spawn_ok) {
                  die "ERROR: premature EOF in login.\n";
                } else {
                  die "ERROR: could not spawn telnet.\n";
                }
              }
             ],
             [
              timeout =>
              sub {
                die "No login.\n";
              }
             ],
             '-re', qr'[#>:] $', #' ожидать приглашения оболочки, затем выходить из expect'а
            );
Как ожидать многострочные порождённые команды
foreach my $cmd (@list_of_commands) {
  push @commands, Expect->spawn($cmd);
}

expect($timeout,
       '-i', \@commands,
       [
        qr"pattern",	       # найти этот образец в выводе всех команд
        sub {
          my $obj = shift;    # соответствующий объект
          print $obj "something\n";
          exp_continue;       # мы не нуждаемся в завершении вызова expect'а
        }
       ],
       '-i', $some_other_command,
       [
        "some other pattern",
        sub {
          my ($obj, $parmref) = @_;
          # ...

          # сейчас мы выходим из команды expect
        },
        \$parm
       ],
      );
Как распространить размеры терминала
my $exp = new Expect;
$exp->slave->clone_winsize_from(\*STDIN);
$exp->spawn("ssh somehost);
$SIG{WINCH} = \&winch;

sub winch {
  $exp->slave->clone_winsize_from(\*STDIN);
  kill WINCH => $exp->pid if $exp->pid;
  $SIG{WINCH} = \&winch;
}

$exp->interact();

ДОМАШНЯЯ СТРАНИЦА

http://sourceforge.net/projects/expectperl/

СПИСКИ РАССЫЛКИ

Доступны два списка рассылки, expectperl-announce и expect- perl-discuss на
http://lists.sourceforge.net/lists/listinfo/expectperl-announce
и
http://lists.sourceforge.net/lists/listinfo/expectperl-discuss

ОТСЛЕЖИВАНИЕ ОШИБОК

Вы можете использовать CPAN Трекер Запросов http://rt.cpan.org/ и добавить новую ошибку под
http://rt.cpan.org/Ticket/Create.html?Queue=Expect

АВТОРЫ

(c) 1997 Остин Шутз (Austin Schutz <ASchutz@users.sourceforge.net>) (отошёл от дел)
Усовершенствование интерфейса и функциональности expect() (c) 1999-2006 Роланд Гирсиг (Roland Giersig).
Данный модуль на данный момент поддерживается Роландом Гирсигом (Roland Giersig) <RGiersig@cpan.org>

ЛИЦЕНЗИЯ

Этот модуль может быть использован под требованиями Perl.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ

ЭТО ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ" И КАКИЕ-ЛИБО ЯВНЫЕ ИЛИ ПОДРАЗУМЕВАЮЩИЕСЯ ГАРАНТИИ, ВКЛЮЧАЯ, НО НЕОГРАНИЧИВАЯСЬ, ПОДРАЗУМЕВАЕМЫМИ ГАРАНТИЯМИ ПРОДАЖИ И ЧАСТИЧНОГО РАСПРОСТРАНЕНИЯ НЕ ОБЕСПЕЧИВАЮТСЯ. В СЛУЧАЕ ОТКАЗА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ АВТОРЫ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ЗА ЛЮБУЮ ПРЯМУЮ, КОСВЕННУЮ, СЛУЧАЙНУЮ, СПЕЦИАЛЬНУЮ, ФИНАНСОВЫЕ ИЛИ ПОСЛЕДУЮЩИЕ ПОВРЕЖДЕНИЯ (ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ОБЕСПЕЧЕНИЕМ ЗАМЕНЫ ТОВАРА ИЛИ УСЛУГИ; ПОТЕРЮ ПРОИЗВОДИТЕЛЬНОСТИ, ДАННЫХ, ИЛИ ФИНАНСОВ; ИЛИ ПРЕРЫВАНИЕ БИЗНЕСА) ОДНАКО ОТВЕТСТВЕННОСТЬ ВО ВСЕХ СЛУЧАЯХ НЕСЁТ ПОЛЬЗОВАТЕЛЬ, НЕЗАВИСИМО ОТ СОБЛЮДЕНИЯ ДОГОВОРА, ИЛИ ЕГО НАРУШЕНИЯ (ВКЛЮЧАЯ ХАЛАТНОСТЬ В ИСПОЛЬЗОВАНИИ И ДРУГИЕ СЛУЧАИ) ВОЗНИКШЕГО В ПРОЦЕССЕ ИСПОЛЬЗОВАНИЯ ЭТОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, И В СЛУЧАЯХ НАМЕРЕННОГО ПРИЧИНЕНИЯ УЩЕРБА.
Другими словами: Вы используете это программное обеспечение на свой страх и риск. Программное обеспечение поставляется "как есть". Читай исходные тексты, Люк (Luke)!
И наконец, для успокоения души:
Любое Использование Этого Продукта, В Абсолютно Любых Способах, Может Увеличить Хаос Во Вселенной. Хотя Отсутствие Ответственности - Это Неуважение Создателя, Потребитель Предупреждён, Что Этот Процесс В Конце Концов Приведёт К Огненной Смерти Вселенной.

perl v5.8.9 2007-07-19 Expect(3)