ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
12.12. Вывод сообщений об ошибках и предупреждений по аналогии со встроенными функциямиПроблемаВаш модуль генерирует ошибки и предупреждения, однако при использовании warn или die пользователь видит имя вашего файла и номер строки. Вы хотите, чтобы функции модуля вели себя по аналогии со встроенными функциями и сообщали об ошибках с точки зрения пользовательского, а не вашего кода.РешениеСоответствующие функции присутствуют в стандартном модуле Carp. Вместо warn используйте функцию carp, а вместо die - функцию croak (для коротких сообщений) или confess (для длинных сообщений).КомментарийНекоторые функции модуля, как и встроенные функции, могут генерировать предупреждения или ошибки. Предположим, вы вызвали функцию sqrt с отри- цательным аргументом (и не воспользовались модулем Math::Complex) - возникает исключение с выводом сообщения вида "Can't take sqrt of -3 at /tmp/ negroot line 17", где /tmp/negroot - имя вашей программы. Но если вы напишете собственную функцию с использованием die:sub even_only { my $n = shift; die "$n is not even" if $n & 1; # Один из способов проверки #.. . . } то в сообщении вместо пользовательского файла, из которого вызывалась ваша функция, будет указан файл, в котором была откомпилирована функция even_only. На помощь приходит модуль Carp. Вместо die мы используем функцию croak: use Carp; sub even_only { my $n = shift; croak "$n is not even" if $n % 2; Другой способ #.... } Если вы хотите просто вывести сообщение с номером строки пользовательской программы, где произошла ошибка, вызовите carp вместо warn (в отличие от warn и die, завершающий перевод строки в сообщениях carp и croak не имеет особой интерпретации). Например: use Carp; sub even_only { my $n = shift; if ($n & 1) { # Проверка нечетности carp "$n is not even, continuing"; ++$n; } # . . . . } Многие встроенные функции выводят предупреждения лишь при использовании ключа командной строки -w. Переменная $"W сообщает о его состоянии. Например, предупреждения можно выдавать лишь при наличии запроса от пользователя: carp "$n is not even, continuing" if $"W; Наконец, в модуле Carp существует третья функция - confess. Она работает аналогично croak за исключением того, что при аварийном завершении выводится полная информация о состоянии стека, вызовах функций и значениях аргументов. > Смотри также -------------------------------
12.13. Косвенные ссылки на пакетыПроблемаТребуется сослаться на переменную или функцию в пакете, имена которых неизвестны до момента выполнения программы, однако синтаксис $packname: : $varname недопустим.РешениеВоспользуйтесь символическими ссылками:{ no strict 'rets'; $val = ${ $packname . "::" . $varname }; Ovals = @{ $packnanie . "::" . $aryname }; &{ $packname . "::" . $funcname }("args"); ($packname . "::" . $funcname) -> ("args"); } КомментарийОбъявление пакета имеет смысл во время компиляции. Если имя пакета или неременной неизвестно до времени выполнения, придется прибегнуть к символическим ссылкам и организовать прямые обращения к таблице символов пакета Включите в блок директиву no strict ' refs' и постройте строку с полным именем интересующей вас переменной или функции. Затем разыменуйте полученную строку так, словно она является нормальной ссылкой Perl. До выхода Perl версии 5 программистам в подобных случаях приходилось использовать eval:eval "package $packname; \$'$val = \$$varname"; # Задать $main'val die if $@; Как видите, такой подход затрудняет построение строки. Кроме того, такой кол работает относительно медленно. Впрочем, вам никогда не придется делать это лишь для того, чтобы косвенно обращаться к переменным по именам. Символические ссылки обеспечивают необходимый компромисс. Функция eval также используется для определения функций во время выполнения программы. Предположим, вы хотите иметь возможность вычислять двоичные и десятичные логарифмы: printf "log2 of 100 is %.2f\n", log2(100): printf "log-IO of 100 is %.2f\n", log10(100); В Perl существует функция log для вычисления натуральных логарифмои. Давайте посмотрим, как использовать eval для построения функций во время выполнения программы. Мы создадим функции с именами от log2 до log999: $packname = 'main'; for ($i =2; $i < 1000; $i++) { $logN = log($i); eval "sub ${packname}::log$i { log(shift) / $logN }"; die if $@; По крайней мере в данном случае это не нужно. Следующий фрагмент делает то же самое, но вместо того, чтобы компилировать новую функцию 998 раз, мы откомпилируем ее всего единожды в виде замыкания. Затем мы воспользуемся символическим разыменованием в таблице символов и присвоим одну и ту же ссылку на функцию по многим именам: $packname = 'main'; for ($i =2; $i < 1000; $i++) { my $logN = log($i); no strict 'rets'; *{"${packname}::log$i"} = sub { log(shift) / $logN }; } Присваивая ссылку тип-глобу, вы всего лишь создаете синоним для некоторого имени. На этом принципе построена работа Exporter. Первая строка следу юще го фрагмента вручную экспортирует имя функции Colors::blue в текущий пакет Вторая строка назначает функцию main: :blue синонимом функции Colors; : azure. *blue = \&Colors::blue; -main::blue = \&Colors::azure; Принимая во внимание гибкость присваивании тип-глобов и символических ссылок, полноценные конструкции eval "СТРОКА" почти всегда оказываются излишеством, последней надеждой отчаявшегося программиста. Ничего худшего себе и представить нельзя - разве что если бы они были недоступны. > Смотри также -------------------------------
12.14. Применение h2ph для преобразования заголовочных файловПроблемаПолученный от кого-то код выдает устрашающее сообщение об ошибке: Can't locate sys/syscall. ph in @INC (did you run h2ph?)(@INC contains: /usr/lib/perl5/i686-linux/5.00404 /usr/lib/perl5 /usr/lib/perl5/site_perl/i686-linux /usr/lib/perl5/site_perl .) at some_program line 7. Вы хотите понять, что это значит и как справиться с ошибкой. РешениеПопросите системного администратора выполнить следующую команду с правами привилегированного пользователя:% cd /usr/include; h2ph sys/syscall.h Однако многие заголовочные файлы включают другие заголовочные файлы; иными словами, придется преобразовать их все: % cd /usr/include; h2ph *.h */*.h Если вы получите сообщение о слишком большом количестве файлов или если некоторые файлы в подкаталогах не будут найдены, попробуйте другую команду: % cd /usr/include; find . -name '*.h' -print | xargs h2ph КомментарийФайлы с расширением .ph создаются утилитой h2ph, которая преобразует директивы препроцессора С из #include-файлов в Perl. Это делается для того, чтобы программа на Perl могла работать с теми же константами, что и программа на С. Утилита h2xs обычно оказывается более удачным решением, поскольку вместо кода Perl, имитирующего код С, она предоставляет откомпилированный код С. Однако работа с h2xs требует намного большего опыта программирования (по крайней мере, в том, что касается С), чем h2ph. Если процесс преобразования h2ph работает, все прекрасно. Если нет - что ж, вам не повезло. Усложнение системных архитектур и заголовочных файлов приводит к более частым отказам h2ph. Если повезет, необходимые константы уже будут присутствовать в модулях Fcnti, Socket или POSIX. В частности, модуль POSIX реализует константы из sys/file.h, sys/ermo.h и sys/wait.h. Кроме того, он обеспечивает выполнение нестандартных операций с терминалом (см. рецепт 15.8).Так что же можно сделать с файлом .ph? Рассмотрим несколько примеров. В первом примере непереносимая функция syscall используется для вызова системной функции gettimeofday. Перед вами реализация модуля FineTime, описанного в рецепте 12.11. # Файл FineTime.pm package main; require 'sys/syscall.ph'; die "No SYS_gettimeofday in sys/syscall.ph" unless defined &SYS_gettimeofday; package FineTime; use strict; require Exporter; use vars qw(@ISA @EXPORT_OK); @ISA = qw(Exporter); @EXPORT_OK = qw(time); sub time() { my $tv = pack("LL", ()); # presize buffer to two longs syscall(&main::SYS_gettimeofday, $tv, undef) >= 0 or die "gettimeofday: $!"; my($seconds, $microseconds) = unpack("LL", $tv); return $seconds + ($microseconds / 1_000_000); } 1; Если вам приходится вызывать require для старых файлов ,рl или .ph, сделайте это из главного пакета (package main в приведенном выше коде). Эти старые библиотеки всегда помещают свои символические имена в текущий пакет, a main служит "местом встречи". Чтобы использовать имя, уточните его, как мы поступили с main::SYS_gettimeofday. Файл sys/ioctl.ph, если вам удастся построить его в своей системе, открывает доступ к функциям ввода/вывода вашей системы через функции loctl. К их числу принадлежит функция TIOCSTI из примера 12.1. Сокращение TIOCSTI означает "управление терминальным вводом/выводом, имитация терминального ввода" (terminal I/O control, simulate terminal input). В системах, где эта функция реализована, она вставляет один символ в поток устройства, чтобы при следующем чтении из устройства со стороны любого процесса был получен вставленный символ. Пример 12.1. jam #!/usr/bin/perl -w # jam - вставка символов в STDIN require 'sys/ioctl.ph'; die "no TIOCSTI" unless defined &TIOCSTI; sub jam { local $SIG{TTOU} = "IGNORE"; # "Остановка для вывода на терминал" local *TTY; # Создать локальный манипулятор open(TTY, "+for (split(//, $_[0])) { vioctl(TTY, &TIOCSTI, $_) or die "bad TIOCSTI: $!"; } close(TTY); } Jam("@ARGV\n"); Поскольку преобразование sys/ioctl.h может вызвать некоторые сложности, вероятно, для получения кода TIOCSTI вам придется запустить следующую программу на С: % cat > tio.c "EOF && ее tio.c && a.out #include main() { printf("%#08x\n", TIOCSTI); } EOF 0х005412 Функция iocti также часто применяется для определения размеров текущего окна в строках/столбцах и даже в пикселях. Исходный текст программы приведен в примере 12.2. Пример 12.2. winsz #!/usr/bin/perl # winsz - определение размеров окна в символах и пикселях require 'sys/ioctl.ph'; die "no TIOCGWINSZ " unless defined &TIOCGWINSZ; open(TTY, "+unless (ioctl(TTY, &TIOCGWINSZ, $winsize='o)) { die sprintf "$0: iocti TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ; } ' ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize); print "(row,col) = ($row,$col)"; print " (xpixel.ypixel) = ($xpixel,$ypixel)" if $xpixel || $ypixel; print "\n"; Как видите, для экспериментов с файлами .ph, распаковкой двоичных данных и вызовами syscall и iocti необходимо хорошо знать прикладной интерфейс С, обычно скрываемый Perl. Единственное, что требует такого же уровня знаний С -это интерфейс XS. Одни считают, что программисты должны бороться с искушением и за версту обходить подобные непереносимые решения. По мнению других, жесткие требования, поставленные перед рядовым программистом, оправдывают самые отчаянные меры. К счастью, все большее распространение получают менее хрупкие механизмы. Для большинства этих функций появились модули CPAN. Теоретически они работают надежнее, чем обращения к файлам .ph. > Смотри также -------------------------------
12.15. Применение h2xs для создания модулей с кодом СПроблемаВам хотелось бы работать с функциями С из Perl.РешениеВоспользуйтесь утилитой h2xs для построения необходимых файлов шаблонов, заполните их соответствующим образом и введите: % perl Makefile.PL % makeКомментарийПри написании модуля Perl необязательно ограничиваться одним Perl. Как и для любого другого модуля, выберите имя и вызовите для него утилиту h2xs. Мы со- здаднм функцию FineTime::time с той же семантикой, что и в предыдущем рецепте, но на этот раз реализуем ее на С. Сначала выполните следующую команду:% h2xs -en FineTime Если бы у нас был файл .h с объявлениями прототипов функций, его можно было бы включить, но поскольку мы пишем модуль с нуля, используется флаг -с - тем самым мы отказываемся от построения кода, преобразующего директивы #define. Флаг -п требует создать для модуля каталог FineTime/, в котором будут находиться следующие файлы: Файл Список файлов в поставке Changes Протокол изменений Makefile.PL MeTa-make-файл FineTime.pm Компоненты Perl FineTime.xs Будущие компоненты С test.pl______Тестовая программа__ Перед тем как вводить команду make, необходимо сгенерировать make-файл для текущей системной конфигурации с помощью шаблона Makefile.PL. Вот как :)TO делается: % perl Makefile.PL Если код XS вызывает библиотечный код, отсутствующий в нормальном наборе библиотек Perl, сначала добавьте в Makefile.?! новую строку. Например, если мы хотим подключить библиотеку librpm.a из каталога /usr/redhat/lib, то нам надо изменить строку Makefile.PL: 'LIBS' =>["], # e.g. , '-1m' и привести ее к виду: 'LIBS' => ['-L/usr/redhat/lib -Irpm'], Наконец, отредактируйте файлы FineTime.pm и FineTime.xs. В первом случае большая часть работы уже сделана за нас. Нам остается создать список экспортируемых функций. На этот раз мы помещаем его в @EXPORT_OK, чтобы нужные функции запрашивались пользователем по имени. Файл FineTime.pm выглядит так: package FineTime; use strict; use vars qw($VERSION @ISA @EXPORT_OK); require Exporter; require DynaLoader; @ISA = qw(Exporter DynaLoader); @EXPORT_OK = qw(time); $VERSION = 'О.О1'; bootstrap FineTime $VERSION; 1; Make автоматически преобразует файл FineTime.xs в FineTime.c и общую библиотеку, которая на большинстве платформ будет называться FineTime.so. Преобразование выполняется утилитой xsubpp, описанной в ее собственной странице руководства Hperlxstut(i). Xsubpp автоматически вызывается в процессе построения. Кроме хороших познаний в С, вы также должны разбираться в интерфейсе С-Рег1, который называется XS (eXternal Subroutine). Подробности и нюансы XS выходят за рамки этой книги. Автоматически сгенерированный файл FineTimeJCs содержит заголовочные файлы, специфические для Perl, а также объявление MODULE. Мы добавили несколько дополнительных файлов и переписали код новой функции time. На С пока не похоже, но после завершения работы xsubpp все придет в норму. Использованный нами файл FineTime.xs выглядит так: #include #include #include "EXTERN.h" #include "perl.h" #include "XSUB.h" MODULE = FineTime PACKAGE = FineTime double time() CODE: struct timeval tv; gettimeofday(&tv,0); RETVAL = tv.tv_sec + ((double) tv,tv_usec) / 1000000; OUTPUT: RETVAL Определение функции с именем, присутствующем в стандартной библиотеке С, не вызовет проблем при компиляции - это не настоящее имя, а лишь псевдоним, используемый Perl. Компоновщик С увидит функцию с именем XS_FineTime_ time, поэтому конфликта не будет. При выполнении команды make install происходит следующее (с небольшими исправлениями): % make install mkdir ./blib/lib/auto/FineTime cp FineTime.pm ./blib/lib/FineTime.pm /usr/local/bin/perl -I/usr/lib/perl5/i686-linux/5.00403 -I/usr/lib/perl5 /usr/lib/perl5/ExtUtils/xsubpp -typemap /usr/lib/perl5/ExtUtils/typemap FineTime.xs FineTime.tc && mv FineTime.tc FineTime.ccc -c -Dbool=char -DHAS_BOOL -02-DVERSION=\"0.01\" -DXS_VERSION-\"0.01\" -fpic -I/usr/lib/perl5/i686-linux/5.00403/CORE FineTime.cRunning Mkbootstrap for FineTime () chmod 644 FineTime.bs LD_RUN_PATH="" cc -o blib/arch/auto/FineTime/FineTime.so -shared -L/usr/local/lib FineTime.о chmod 755 blib/arch/auto/FineTime/FineTime.so ср FineTime.bs ,/blib/arch/auto/FineTime/FineTime. bs chmod 644 blib/arch/auto/FineTime/FineTime.bs Installing /home/tchrist/perllib/i686-linux/./auto/FineTime/FineTime.so Installing /home/tchrist/perllib/i686-linux/./auto/FineTime/FineTime.bs Installing /home/tchrist/perllib/./FineTime.pm Writing /home/tenrist/perllib/i686-linux/auto/FineTime/.packlist Appending installation info to /home/tchrist/perllib/i686-linux/perllocal.pod Когда все будет готово, в интерпретаторе вводится следующая команда: % perl -I ""/perllib -MFineTime=time -le '1 while print time()' | head 888177070.090978 888177070.09132 888177070.091389 888177070.091453 888177070.091515 888177070.091577 888177070.091639 888177070.0917 888177070.091763 888177070.091864 > Смотри также --------------------------------
12.16. Документирование модуля в формате podПроблемаВы хотите документировать свой модуль, но не знаете, какой формат следует использовать.РешениеВключите документацию в файл модуля в формате pod.КомментарийСокращение pod означает "plain old documentation", то есть "простая документация". Документация в формате pod включается в программу с применением очень простого формата разметки. Как известно, программисты сначала пишут программу, а документацию... не пишут вообще. Формат pod был разработан для максимальной простоты документирования, чтобы с этой задачей справился даже лентяй. Иногда это даже помогает. Если во время анализа исходного текста Perl обнаруживает строку, начинающуюся со знака = (там, где ожидается новая команда), он игнорирует весь текст до строки, начинающейся с =cut, после чего продолжает анализировать код. Это позволяет смешивать в программах или файлах модулей Perl код и документацию. Поскольку формат pod является сугубо текстовым, никакого особого форматирования не требуется. Трансляторы стараются проявить интеллект и преобразуют вывод так, чтобы программисту не приходилось особым образом форматировать имена переменных, вызовы функций и т. д.Вместе с Perl поставляется несколько программ-трансляторов, которые фильтруют документацию в формате pod и преобразуют ее в другой формат вывода. Утилита pod2man преобразует pod в формат troff, используемый в программе man или в системах верстки и печати. Утилита pod2html создает Web-страницы, работающие в системах, не принадлежащих к семейству UNIX. Утилита pod2text преобразует pod в простой ASCII-текст. Другие трансляторы (pod2ipf, pod2fm, pod2texf pod2latex и pod2ps) могут входить в поставку Perl или распространяются чере:' CPAN. Многие книги пишутся в коммерческих текстовых редакторах с ограниченными сценарными возможностями... но только не эта! Она была написана в формак pod в простых текстовых редакторах (Том использовал vi, а Нат - emacs). На стадии технической правки книга была преобразована в формат troff специальным транслятором pod2ora, написанным Ларри. Окончательный вариант книги были получен преобразованием pod-файлов в формат FrameMaker. Хотя в perlpod{1) приведено общее описание pod, вероятно, этот формат удобнее изучать на примере готовых модулей. Если вы начали создавать собственные модули с помощью утилиты h2xs, то у вас уже имеются образцы. Утилита Makefile знает, как преобразовать их в формат man и установить страницы руководства так, чтобы их могли прочитать другие. Кроме того, программа perldoc может транслировать документацию pod с помощью pod2text. Абзацы с отступами остаются без изменений. Другие абзацы переформатируются для размещения на странице. В pod используются лишь два вида служебной разметки: абзацы, начинающиеся со знака = и одного или нескольких слов, и внутренние последовательности в виде буквы, за которой следует текст в угловых скобках. Теги абзацев определяют заголовки, перечисляемые элементы списков и служебные символы, предназначенные для конкретного транслятора. Последовательности в угловых скобках в основном используются для изменения начертания (например, выбора полужирного, курсивного или моноширинного шрифта), Приведем пример директивы =head2 в сочетании с изменениями шрифта: =head2 Discussion If we had a dot-h file with function prototype declarations, we could include that, but since we're writing this one from scratch, we'll use the -c flag to omit building code to translate any #define symbols. The -n flag says to create a module directory named FineTime/, which will have the following files. Последовательность =for определяет код для выходных файлов конкретного формата. Например, в этой книге, главным образом написанной в формате pod, присутствуют вызовы стандартных средств troff: eqn, tbi и pie. Ниже показан пример внутреннего вызова eqn, который обрабатывается лишь трансляторами, производящими данные в формате troff: =for troff .ЕО log sub n (x) = { {log sub e (x)} over {log sub e (n)} } .EN Формат pod также позволяет создавать многострочные комментарии. В языке С комментарий /*. . . . */ может включать несколько строк текста - вам не придется ставить отдельный маркер в каждой строке. Поскольку Perl игнорирует директивы pod, этим можно воспользоваться для блочного комментирования. Весь фокус заключается в том, чтобы найти директиву, игнорируемую трансляторами pod. Например, можно воспользоваться тегом for later или for nobody: =for later next if 1 . . ?"$'?; s/"(.)/>$1/, s/(.{73})........*/$1 =cut back to perl или парой ^begin и =end: =begin comment if (!open(FILE, $file)) { unless ($opt_q) { warn "$me: $file: $!\n"; $Errors++; } next FILE; } $total = 0; $matches = 0: =end comment > Смотри также
12.17. Построение и установка модуля CPANПроблемаТребуется установить файл модуля, загруженный с CPAN или взятый с компакт-диска.РешениеВведите в интерпретаторе следующие команды (на примере установки модуля Some::Module версии 4.54):% gunzip Some-Module-4.54.tar.gz % tar xf Some-Module-4.54 % cd Some-Module-4.54 % perl Makefile.PL % make % make test % make install КомментарийМодули Perl, как и большинство программ в Сети, распространяются в архивах tar, сжатых программой GNU zip1. Если tar выдает предупреждение об ошибках контрольных сумм каталогов ("Directory checksum errors"), значит, вы испортили двоичный файл, приняв его в текстовом формате.Вероятно, для установки модуля в системные каталоги необходимо стать привилегированным пользователем с соответствующими правами доступа. Стандартные модули обычно устанавливаются в каталог /usr/lib/perl5, а прочие - в каталог /usr/lib/perl5/site_perl. Рассмотрим процесс установки модуля MD5: % gunzip MD5-1.7.tar.gz % tar xf MD5-1.7.tar % cd MD5-1.7 % perl Makefile.PL Checking if your kit is complete... Looks good Writing Makefile for MD5 X make mkdir ./blib mkdir ./blib/lib cp MDS.pm ./blib/lib/MDS.pm AutoSplitting MD5 (,/blib/lib/auto/MD5) /usr/bin/perl -I/usr/local/lib/perl5/i386 cp MD5.bs ./blib/arch/auto/MD5/MD5.bs Ghmod 644 ./blib/arch/auto/MD5/MD5.bsmkdir ./blib/man3 Manifying ,/blib/man3/MD5.3 % make test PERL_DL_NONLAZY=1 /usr/bin/perl -I./blib/arch -I./blib/lib -I/usr/local/lib/perl5/i386-freebsd/5.00404 -I/usr/local/lib/perlS test.pi 1..14 ok 1 ok 2 . . . ok 13 ok 14 % sudo make install Password: Installing /usr/local/lib/perlS/site.perl/iSBe-freebsd/./auto/MDS/ MDS.so Installing /us(71ocal/lib/perl5/site_perl/i386-freebsd/./auto/MD5/ MDS.bs Installing /usr/local/lib/perl5/site_perl/./auto/MD5/autosplit.ix Installing /usr/local/lib/perl5/site_perl/./MD5.pm Installing /usr/local/lib/perl5/man/man3/./MD5.3 Writing /usr/local/lib/perl5/site_perl/i386-freebsd/auto/MD5/.packlist Appending installation info to /usr/local/lib/perl5/i386-freebsd/ 5,00404/perllocal.pod Если ваш системный администратор где-то пропадает или у него нет времени на установку, не огорчайтесь. Используя Perl для построения .make-файла по шаблону Makefile.PL, можно выбрать альтернативный каталог для установки. # Если вы хотите установить модули в свой каталог % perl Makefile.PL LIB=~/lib # Если у вас имеется полная поставка % perl Makefile.PL PREFIX=~/perl5-private > Смотри также -------------------------------
12.18. Пример: шаблон модуляНиже приведен "скелет" модуля. Если вы собираетесь написать собственный модуль, попробуйте скопировать и отредактировать его.package Some::Module; # Должен находиться в Some/Module.pm use strict; require Exporter; use vars qw($VERSION @ISA ©EXPORT @>EXPORT_OK %EXPORT_TAGS); # Установка версии для последующей проверки $VERSION = 0.01; @ISA = qw(Exporter); @EXPORT = qw(&func1 &func2 &func4); %EXPORT_TAGS = ( ); # например: TAG => [ qw!name-l name2! # Здесь находятся экспортируемые глобальные переменные, # а также функции с необязательным экспортированием @EXPORT_OK = qw($Var1 %Hashit &func3); use vars qw($Var1 %Hashit); # Здесь находятся неэкспортируемые глобальные имена пакета use vars qw(@more $stuff); # Инициализировать глобальные переменные пакета, # начиная с экспортируемых $Var1 = ''; %Hashit =(); # Затем все остальные (к которым можно обращаться 3 в виде $Some::Module::stuff) $stuff = ''; @more =(); # Все лексические переменные с файловой областью действия # должны быть созданы раньше функций, которые их используют. # Лексические переменные, доступ к которым # ограничивается данным файлом. my $priv_var = ''; mу %secret_hash = (); # Закрытая функция, оформленная в виде замыкания # и вызываемая через &$priv_func. my $priv_func = sub { # Содержимое функции. } # Все ваши функции, экспортируемые и нет; # не забудьте вставить что-нибудь в заглушки {} sub fund {....} # без прототипа sub func2() {....} # прототип - void sub func3($$) {....} # прототип - 2 скаляра # Функция не экспортируется автоматически, но може! вызываться! sub func4(\%) {....} # прототип - 1 ссылка на хэш END { } # Завершающий код модуля (глобальный деструктор) 1; 12.19. Программа: поиск версий и описаний установленных модулейPerl распространяется вместе с множеством модулей. Еще больше модулей можно найти в CPAN. Следующая программа выводит имена, версии и описания всех модулей, установленных в вашей системе. Она использует стандартные модули (например, File::Find) и реализует некоторые приемы, описанные в этой главе. Программа запускается следующей командой: % pmdesc Она выводит список модулей с описаниями:FileHandle (2.00) - supply object methods for filehandles 10 :File (1.06021) - supply object methods for filehandles 10 :Select (1.10) - 00 interface to the select system call 10 :Socket (1.1603) - Object interface to socket communications. . . С флагом -v программа pmdesc выводит имена каталогов, в которых находятся файлы: % pmdesc -v << FileHandle (2.00) - supply object methods for filehandles . . . Флаг -w предупреждает о том, что модуль не включает документации в формате pod, а флаг -s сортирует список модулей в каждом каталоге. Исходный текст программы приведен в примере 12.3. Пример 12.3. pmdesc #!/usr/bin/perl -w # pmdesc - вывод описаний файлов pm # tchrist@perl.conn use strict; use File::Find qw(find); use Getopt::Std qw(getopts); use Carp; use vars ( q!$opt_v! # Вывод отладочной информации q!$opt_w! # Предупреждения об отсутствующих # описаниях модулей q!$opt_a! # Вывод относительных путей q!$opt_s! # Сортировка данных по каждому каталогу }; $| = 1; getopts('wvas') or die "bad usage"; @ARGV = @INC unless OARGV; # Глобальные переменные. Я бы предпочел обойтись без этого. use vars ( q!$Start_Dir!, # Каталог верхнего уровня, для которого # вызывалась функция find q!%Future!, # Другие каталоги верхнего уровня, # для которых find вызывается позднее ); my $Moauie, # Установить фильтр для сортировки списка модулей, # если был указан соответствующий флаг. if ($opt_s) { if (open(ME, "- ")) { $/ = o o; while ( print join("\n", sort split /\n/), "\n"; } exit; } } MAIN: { my %visited; my ($dev,$ino); @Future{@ARGV} = (1) x OARGV; foreach $Start_Dir (@ARGV) { delete $Future{$Start_Dir}; print "\n"Modules from $Start_Dir"\n\n" if $opt_v; next unless ($dev,$ino) = stat($Start_Dir); next if $visited{$dev,$ino}++; next unless $opt_a |[ $Start_Dir =~ m!"/!; find(\&wanted, $Start_Dir); } exit; } # Вычислить имя модуля по файлу и каталогу sub modname { local $_ = $File::Find::name; if (index($_, $Start_Dir . '/') == 0) { substr($_, 0, 1+length($Start_Dir)) = } s { / } {::}gx, s { \.p(m|od)$ } {}x; return $_: } # Решить, нужен ли нам данный модуль sub wanted { if ( $Future{$File::Find::name} ) { warn "\t(Skipping $File::Find::name, qui venit in future.)\n" if 0 and $opt_v; $File::Find::prune = 1; return: } return unless /\.pm$/ && -f; $Module = &modname; # skip obnoxious modules if ($Module =~ /"CPAN(\Z|::)/) { warn("$Module -- skipping because it misbehaves\n"); return; } my $file = $_; unless (open(POD, "< $file")) { warn "\tcannot open $file: $! # if $opt_w; return 0; } $:=" -:"; local $/ = oo; local $_; while ( if (/=head\d\s+NAME/) chomp($_ = s/".*'?-\s+//s; vs/\n/ /g; #write; my $v, if (defined ($v = getversion($Module))) { print "$Module ($v) "; } else { print "$Module "; } print "- $_\n"; .return 1; } } warn "\t(MISSING DESC FOR $File::Find::name)\n' if $opt_w; return 0; } # Загрузить модуль и вывести его номер версии, # перенаправляя ошибки в /dev/null sub getversion { my $mod = shift: my $vers = '$"X -m$mod -e 'print \$${mod}::VERSION' 2>/dev/null $vers =` s/"\s*(. *?)\s*$/$1/; # Удалить лишние пропуски return ($vers || undef); } format = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $Module, $_
|