Глава 12 Пакеты, библиотеки и модули

Пакеты, библиотеки и модули

Введение

Представьте, что у вас есть две программы, каждая из которых хорошо работает сама по себе. Возникает идея - создать третью программу, объединяющую лучшие свойства первых двух. Вы копируете обе программы в новый файл и начинаете перемещать фрагменты. Выясн Проблема решается с помощью пакетов. Пакеты используются в Perl для разделения глобального пространства имен. Они образуют основу как для традиционных модулей, так и для объектно-ориентированных классов. Подобно тому, как каталог содержит файлы, пакет сод Ключевое слово package является объявлением, обрабатываемым на стадии компиляции. Оно устанавливает префикс пакета по умолчанию для неполных глобальных идентификаторов, по аналогии с тем, как chdir устанавливает префикс каталога по умолчанию для относител
package Alpha;
$name = "first";
package Omega;
$name = "last";
package main;
print "Alpha is $Alpha::name, Omega is $0mega::name.\n";
Alpha is first, Omega is last.

В отличие от пользовательских идентификаторов, встроенные переменные о специальными именами (например, $_ и $.) и идентификаторы STDIN, STDOLT. STDERR, ARGV, ARGVOUT, ENV, INC и SIG без указания имени пакета считаются принадлежащими к пакету main. Бла

Модули

Многократное использование кода в Perl осуществляется с помощью модулей Модуль представляет собой файл, содержащий набор взаимосвязанных функций, которые используются другими программами и библиотечными модулями, У каждого модуля имеется внешний интерфейс Команды require и use подключают модуль к вашей программе, хотя и оГ). ,, ;:i-ют несколько разной семантикой. Команда require загружает модуль во и; \i/i выполнения с проверкой, позволяющей избежать повторной загрузки модуля. Команда use работает аналогично, но с двумя дополнительными свойствами: загрузкой модуля на стадии компиляции и автоматическим импортированием. Модули, включаемые командой use, обрабатываются на стадии компиляции, а обработка require происходит во время выполнения. Это существенно, поскольку при отсутствии необходимого модуля программа даже не запустится - use не пройдет компиляцию сценария. Друг Обработка команды use на стадии компиляции позволяет передавать указания компилятору. Директива (pragma) представляет собой специальный модуль, влияющий на процесс компиляции Perl-кода. Имена директив всегда записываются в нижнем регистре, поэтому при нап Другое отличие use и require заключается в том, что use выполняет неявное импортирование пакета включаемого модуля. Импортирование функции или переменной из одного пакета в другой создает некое подобие синонима - иначе говоря, появляются два имени, обозна Модули Perl должны иметь расширение .рт. Например, модуль FileHaridle хранится в файле FileHandle.pm. Полный путь к файлу зависит от включаемых путей, хранящихся в глобальном массиве @>INC. В рецепте 12.7 показано, как работать с этим массивом. Если имя модуля содержит одну или несколько последовательностей ::, они преобразуются в разделитель каталогов вашей системы. Следовательно, модуль File::Find в большинстве файловых систем будет храниться в файле File/Find.pm. Например:
require "FileKande.pm";
require Filetiandle;
use FileHandle;
require "Cards/Poker.pm";
require Cards::Poker;
use Garde::Poker;
# Загрузка во время выполнения
# Предполагается ".pm";
# то же, что и выше
# Загрузка во время компиляции
# Загрузка во время выполнения
# Предполагается ".рm";
# то ine, что и выше
# Загрузка во время компиляции
Правила импортирования/экспортирования Процесс экспортирования демонстрируется ниже на примере гипотетического мод\ ля Cards::Poker. Программа хранится в файле Poker.pm в каталоге Cards, то есть Cards/ Poker.pm (о том, где должен находиться каталог Cards, рассказано в рецепте 12.7). Приведем с
1 package Cards::Poker;
2 use Exporter: 3
@ISA = ('Exporter');
4 @EXPORT = qw(&shuffle @card_deck);
5 @card_deck = (); # Инициализировать глобальные
# переменные пакета
6 sub shuffle { } # Определение
# заполняется позднее
7 1; #Не забудьте!

В строке 1 объявляется пакет, в который модуль поместит свои глобальные неременные и функции. Обычно модуль начинается с переключения на конкретный пакет, что позволяет ему хранить глобальные переменные и функции так, чтобы они не конфликтовали с пере Не пишите'package Poker только потому, что модуль хранится в файле Poker.pm'. Используйте package Cards:: Poker, поскольку в пользовательской программе будет стоять команда use Cards:: Poke r. Эту распространенную ошибку трудно обнаружить. Если между кома Строка 2 загружает модуль Exporter, управляющий внешним интерфейсом модуля (см. ниже). Строка 3 инициализирует специальный, существующий на уровне пакета массив @ISA строкой "Exporter". Когда в программе пользователя встречается команда use Cards: : Poker Строка 4 заносит список ('&shuffle', '@card_deck') в специальный, существующий на уровне пакета массив ©EXPORT. При импортировании модуля для переменных и функций, перечисленных в этом массиве, создаются синонимы в вызывающем пакете. Благодаря этому после Строки 5 и 6 готовят глобальные переменные и функгщи пакета к экспорч про-ванию (конечно, вы предоставите более конкретные инициализации и onpe.ii.ie- ния, чем в нашем примере). Добавьте другие переменные и функции, включая и те, которые не были включены в внешний интерфейс посредством @EXPORT. Об использовании модуля Exporter рассказано в рецепте 12.1. Наконец, строка 7 определяет общее возвращаемое значение модуля. В нашем случае это просто 1. Если последнее вычисляемое выражение модуля не дает истинного значения, инициируется исключение. Обработка исключений рассматривается в рецепте 12.2. Подойдет лю Пакеты обеспечивают группировку и организацию глобальных идентификаторов. Они не имеют ничего общего с ограничением доступа. Код, откомпилированный в пакете Church, может свободно просматривать и изменять переменные пакета State. Пакетные переменные всегд Другие типы библиотечных файлов Библиотека представляет собой набор неформально взаимосвязанных функций, используемых другими программами. Библиотеки не обладают жесткой семантикой модулей Perl. Их можно узнать по расширению файла .pi - например, syslog.pl и chat2.pl. Библиотека Perl (а в сущности, любой файл, содержащий код Perl) может загружаться командой do 'file.pl' или require ' f 11, pi'. Второй вариант лучше, поскольку в отличие от do require выполняет неявную проверку ошибок. Команда инициирует исключение, если Библиотеки хорошо работают в программах, однако в ситуациях, когда одна библиотека использует другую, могут возникнуть проблемы. Соответственно, простые библиотеки Perl в значительной степени устарели и были заменены более современными модулями. Однако не В Perl встречаются и другие расширения файлов. Расширение .ph используется для заголовочных файлов С, преобразованных в библиотеки Perl утилитой oi2ph (см. рецепт 12.14). Расширение л'з соответствует исходному файлу С (возможно, созданному утилитой h2xs), скомпилированному утилитой xsubpp и компилятором С в машинный код. Процесс создания смешанных модулей рассматривается в рецепте 12.15. До настоящего времени мы рассматривали лишь традиционные модули, которые экспортируют свой интерфейс, предоставляя вызывающей стороне прямой oступ к некоторым подпрограммам и переменным. К этой категории относится большинство модулей. Но некоторые задачи - и некоторые программисты - связываются с хитроумными модулями, содержащими объекты. Объектно-ориентированный модуль редко использует механизм импортирования/экспортирования. Вместо этого он предоставляет объектно Пользуйтесь готовыми решениями CPAN (Comprehensive Perl Archive Network) представляет собой гигантское хранилище практически всех ресурсов, относящихся к Perl, - исходные тексты, документацию, версии для альтернативных платформ и, что самое главное, модули. Перед тем как браться за нов На CPAN можно обратиться по адресу http://www.perl.com/CPAN/CPAN.html(или ftp://www.perl.com/pub/perl/CPAN/CPAN.html). В этом файле кратко описан каждый модуль, входящий в CPAN. Поскольку файл редактируется вручную, в нем могут отсутствовать описания посл Каталог модулей находится по адресу CPAN/modules. В нем содержатся индек-сы всех зарегистрированных модулей, а также имеются три удобных подкаталога: by_module (сортировка по модулям), by_author (сортировка по авторам) и by_category (сортировка по категор

> Смотри также --------------------------------
Разделы "Packages" и "Modules" в perlmod(1).

12.1. Определение интерфейса модуля

Проблема

Требуется определить внешний интерфейс модуля с помощью стандартного модуля Exporter.

Решение

Включите в файл модуля (например, YourModule.pm) приведенный ниже фрагмент. Многоточия заполняются в соответствии с инструкциями, приведенными в разделе "Комментарий". package YourModule;
use strict;
use vars qw(@ISA OEXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
use Exporter;
$VERSION =1.00; # Или выше
@ISA = qw(Exporter);
@EXPORT = qw(...); # Автоматически экспортируемые имена
# (набор :DEFAULT)
@EXPORT_OK = qw(...); # Имена, экспортируемые по запросу
%EXPORT_TAGS = ( # Определение имен для наборов TAG1 => [...], TAG2 => [,..],
###################
# Ваш программный код
##################
1; # Так должна выглядеть последняя строка
Чтобы воспользоваться модулем YourModule в другом файле, выберите один из следующих вариантов:
use YourModule; # Импортировать в пакет имена по умолчанию
use YourModule qw(..,); # Импортировать в пакет перечисленные имена
use YourModule (); # Не импортировать никаких имен use YourModule
qw(:TAG1); # Импортировать набор имен

Комментарий

Внешний интерфейс модуля определяется с помощью стандартного модуля Exporter. Хотя в пакете можно определить собственный метод import, почти никто этого не делает. Когда в программе встречается команда use YourModule, в действительности выполняется команда require "YourModule, pm", за которой вызывается метод ourModule->import(). Это происходит во время компиляции; Метод import, уна-юдованный из пакета Exporter, ище $VERSION
При загрузке модуля можно указать минимальный допустимый номер версии. ели версия окажется ниже, use инициирует исключение. use YourModule 1.86 # Если $VERSION < 1.86, происходит исключение

$EXPORT


Массив содержит список функций и переменных, экспортируемых в пространство имен вызывающей стороны, чтобы в дальнейшем к ним можно было обращаться без уточнения имени пакета. Обычно используется список в форме
qw():
@EXPORT = qw(&F1 &F2 @List);
@EXPORT = qw( F1 F2 @List); # To же

После выполнения простой команды use YourModule вы сможете вызывав функцию &F1 в виде F1() вместо YourModule: : F1() и обращаться к массиву @и" иместо ©YourModule: : List. Амперсанд (&) перед спецификацией экспортирован ной функции необязателен. Чтобы загрузить модуль во время компиляции, но при этом запретить экс сортирование каких-либо имен, воспользуйтесь специальной формой с пустым списком use Exporter().
@EXPORT_OK

Массив содержит имена, которые могут импортироваться по конкретному за просу. Если массив заполнен следующим образом:
@EXPORT_OK = qw(Op_Func %Table);

то пользователь сможет загрузить модуль командой:
use YourModule qw(Op_Func %Table F1);

и импортировать только функцию Op_Func, хэш %Table и функцию F1. Функция F1 присутствует в массиве ©EXPORT. Обратите внимание: команда не выполняет автоматического импортирования F2 или @Llst, хотя эти имена присутствуют в ©EXPORT. Чтобы получить все
use YourModule qw(:DEFAULT %Table);
%EXPORT_TAGS

Хэш используется большими модулями (типа CGI или POSIX) для выcокоуровневой группировки взаимосвязанных импортируемых имен. Его значния представляют собой ссылки на массивы символических имен, каждое из которых должно присутствовать либо в @EXPORT, либо в @EXPORT_OK. Приведем пример инциализации:
%EXPORT_TAGS = (
Functions => [ qw(F1 F2 Op_Func) ], Variables => [ qw(@List %Table) ],
):

Импортируемое имя с начальным двоеточием означает импортирование группы имен. Например, команда:
use YourModule qw(:Functions %Table);

импортирует все имена из
@{ $YourModule::EXPORT_TAGS{Functions} },

тo есть функции F1, F2 и Op_Func, а затем - хэш %Table. Хотя тег : DEFAULT не указывается в %EXPORT_TAGS, он обозначает все содержимое @EXPORT. Все эти переменные не обязательно определять в каждом модуле. Ограничьтесь лишь теми, которые будут использоваться.

> Смотри также -------------------------------
Документация по стандартному модулю Exporter; рецепты 12.7; 12.18.

12.2. Обработка ошибок require и use

Проблема

Загружаемый модуль может отсутствовать в системе. Обычно это приводит к фатальной ошибке. Вы хотите обнаружить и перехватить эту ошибку.

Решение

Поместите require или use в eval, a eval - в блок BEGIN:
# Не импортировать
BEGIN {
unless (eval "require $mod") { warn "couldn't load $mod: $@";
}
}
# Импортировать в текущий пакет
BEGIN {
unless (eval "use $mod") { warn "couldn't load $mod: $@;
}
}

Комментарий

Попытка загрузки отсутствующего или неполного модуля обычно должна приводить к аварийному завершению программы. Однако в некоторых ситуациях программа должна продолжить работу - например, попытаться загрузить другой модуль. Как и при других исключениях, д |рименяется конструкция eval. Использовать eval { БЛОК } нежелательно, поскольку в этом случае будут перехватываться только исключения времени выполнения, a use относится к собы-шям времени компиляции. Вместо этого следует использовать конструкцию val "СТРОКА", что позволит перехватывать и ошибки компиляции. Помните: чызов require для простого слова' имеет несколько иной смысл, чем вызов require "Простым словом" (barcword) называется слово, не имеющее специальной грамматической интерпретации и интерпретируемое как строка. - Примеч. перев. для переменной. Команда добавляет расширение .рт и преобразует : : в раздел г тель каталогов вашей операционной системы - в каноническом варианте / (как в URL), но в некоторых системах используются \, : и даже . . Если вы хотите последовательно попытаться загрузить несколько модулей и остановиться на первом работающем, поступите так:
BEGIN {
my($found, @DBs, $mod):
$found = 0;
ODBs = qw(Giant::Eenie Giant::Meanie Mouse::Mynie Мое);
for $mod (@DBs) {
if (eval "require $mod") {
$mod->import(); # При необходимости
$found = 1;
last-
}
}
die "None of @DBs loaded" unless $found:
}

Мы включаем eval в блок BEGIN, чтобы гарантировать загрузку модуля во врс мя компиляции, а не во время выполнения.
> Смотри также
Рецепт 10.12; рецепт 12.3. Функции eval, die, use и require описаны в perlfunc(l).

12.3. Отложенное использование модуля

Проблема

Необходимо организовать загрузку модуля на определенной стадии p;i"n программы или вообще отказаться от его загрузки при некоторых обстоя! , ствах.

Решение

Разбейте use на отдельные компоненты require и import, либо воспольз\! . ь директивой use autouse.

Комментарий

Если программа проверяет свои аргументы и завершает работу с информационным сообщением или ошибкой, загружать неиспользуемые модули бессмысленно. Это лишь вызывает задержки и раздражает пользователей. Но как говори, юсь во введении, команды use обрабатыва Наиболее эффективная стратегия состоит в проверке аргументов внутри блока BEGIN до загрузки модулей. Следующая программа перед загрузкой необходимых модулей проверяет, что она была вызвана ровно с двумя аргументами, каждый из которых является целым числом
BEGIN {
unless (OARGV == 2 && (2 == grep {/"\d+$/}
@ARGV)) { die "usage: $0 num1 num2\n";
}
}
use Some::Module;
use More::Modules;
Похожая ситуация возникает в программах, которые при разных запусках могут использовать разные наборы модулей. Например, программа factors из главы 2 "Числа" загружает библиотеку вычислений с повышенной точностью лишь при вызове с флагом -Ь. Команда u
if ($opt_b) {
require Math::BigInt;
}

Math::BigInt является не традиционным, а объектно-ориентированным модулем, поэтому импортирование не требуется. Если у вас имеется список импортируемых объектов, укажите его в конструкции qw() так, как это было бы сделано для use. Например, вместо:
use Fcnti qw(0_EXCL 0_CREAT 0_RDWR);
можно использовать следующую запись:
require Fcnti;
Fcntl->import(qw(0_EXCL 0_CREAT 0_RDWR));
Откладывая импортирование до времени выполнения, мы сознательно идем на то, что оставшаяся часть программы не узнает об изменениях импортированной семантики, которые были бы видны компилятору при использовании use. В частности, не будут своевременно в Возникает идея - инкапсулировать отложенную загрузку в подпрограмме. Следующее, простое на первый взгляд решение не работает:
sub load_module {
require $_[0]; # HEBEPHO
import $_[0]; # HEBEPHO
}

Понять причину неудачи непросто. Представьте себе вызов require с аргументом "Math: : BigFloat". Если это простое слово, : : преобразуется в разделитель каталогов операционной системы, а в конец добавляется расширение .рт. Но простая переменная интерп
load_module('Fcntl', qw(0_EXCL 0_CREAT 0_RDVJR)),
sub load_module {
eval "require $_[0]":
die if $@;
$_[0]->import(@_[1 .. $"_]);
}

Но и он в общем случае не идеален. Функция должна импортировать имена не в свой пакет, а в пакет вызвавшей стороны. В принципе эта проблема решается, но процедура становится все сложнее и сложнее. Удобное альтернативное решение - применение директивы autouse. Он 1"1-явилась в Perl 5.004. Эта новая директива экономит время для редко загрузи мых функций, откладывая их загрузку до момента фактического использования: use autouse Fcnti => qw( 0_EXCL() 0_CREAT() 0_RDWR() ); Круглые скобки после 0_EXCL, 0_CREAT и 0_RDWR нужны для autouse, но не для :se или import. Директива autouse принимает не только имена функций, но также позволяет передать прототип функции. В соответствии с прототипами константы Fcnti вызываются без аргум Также помните, что проверка use strict осуществляется во время компи;!;: 1, :.i. Если модуль Fcnti подключается командой use, прототипы модуля Fcnti "' I,T откомпилированы и мы сможем использовать константы без круглых civ' '"к Если использована команда r За сведениями об особенностях директивы autouse обращайтесь к электрсгшж документации.

> Смотри также -------------------------------
Рецепт 12.2; документация но стандартному модулю Exporter (описание метода import); документация по стандартной директиве use autouse.

12.4. Ограничение доступа к переменным модуля

Проблема

Требуется сделать переменную или функцию закрытой (то есть разрепг, к' использование только в границах пакета).

Решение

Общего решения не существует. Однако можно ограничить доступ на уровне файла, в котором находится модуль, - обычно этого достаточно.

Комментарий

Помните, что пакет всего лишь определяет способ группировки переменных и функции и потому не поддерживает ограничения доступа. Все содержимое пакета по определению является глобальным и доступным отовсюду. Пакеты лишь группируют, ничего не скрывая. Ограничение доступа возможно только с применением лексических переменных. Предположим, модуль реализован в виде файла Module.pm, а все его глобальные имена принадлежат пакету Module. Поскольку файл по определению образует самостоятельную область действия, Однако переключение пакетов внутри области действия может привести к тому, что лексические переменные этой области остаются видны в любом месте области. Дело в том, что команда package всего лишь устанавливает новый префикс для глобальных идентификаторов.
package Alpha;
my $aa = 10;
$х = "azure";
package Beta;
my $bb = 20;
$x = "blue";
package main;
print "$aa, $bb, $x, $Alpha::x, $Beta::x\n";
10, 20, , azure, blue

На это ли вы рассчитывали? Две лексические переменные, $аа и $bb, остаются в области действия, поскольку они не вышли за границы текущего блока, файла или eval. Считайте, что глобальные и лексические переменные существуют в разных изменениях, никак не Итак, пакеты не позволяют ограничивать доступ - зато на это способны моду-.'щ, поскольку они находятся в файлах, а файл всегда обладает собственной областью действия. Приведенный ниже простой модуль находится в файле Flipper.pni ii экспортирует две функци
# Flipper.pm package Flipper;
use strict;
require Exporter;
use vars qw(@ISA OEXPORT $VERSION);
@ISA = qw(Exporter);
@EXPORT = qw(flip_words flip_boundary);
$VERSION =1.0;
my $Separatrix = ' '; # По умолчанию пробел; предшествует функциям
sub flip_boundary {
my $prev_sep = $Separatrix;
if (@_) { $Separatrix = $_[0] }
return $prev_sep;
} sub flip_words {
my $line = $_[0];
my @words = split($Separatrix, $line);
return join($Separatrix, reverse @words);
}
1;

Модуль задает значения трех пакетных переменных, необходимых для работы Exporter, а также инициализирует лексическую переменную $Separatrix уровня файла. Как говорилось выше, эта переменная ограничивается границами файла, а не пакета. Весь код той же Лексические переменные, существующие в некоторой области действия, нельзя прочитать или изменить вне этой области, которая в данном случае соответствует всему файлу после объявления переменной. На лексические переменные нельзя ссылаться по полному имени и Работа приведенного выше модуля ничуть не изменилась бы, будь $Separa:.' ix пакетной глобальной переменной, а не файловой лексической. Теоретически к ней можно было бы обратиться снаружи так, что модулю об этом ничего не было известно. С другой стороны, н Если уж речь зашла о стиле, регистр символов в идентификаторах модуля Flipper выбирался не случайно. В соответствии с руководством по стилю программирования на Perl. символами верхнего регистра записываются идентификаторы, имеющие специальное значение для

> Смотри также -------------------------------
perlstyle{1); рецепты 10.2-10.3. Лексические переменные с файловой областью действия рассматриваются в perlmod(1).

12.5. Определение пакета вызывающей стороны

Проблема

Требуется узнать текущий или вызывающий пакет.

Решение

Текущий пакет определяется так:
$this_pack = __PACKAGE__;

Пакет вызывающей стороны определяется так:
$that_pack = са11ег();

Комментарий

Метанеременная __PACKAGE__ возвращает пакет, в котором был откомпилирован :екущий код. Значение не интерполируется в строках, заключенных в кавычки: print "I am in package __PACKAGE__\n"; # НЕВЕРНО! I am in package __PACKAGE__ Необходимость узнать пакет вызывающей стороны чаще возникает в старом годе, которому в качестве входных данных была передана строка для eval, файловый манипулятор, формат или имя манипулятора каталога. Рассмотрим гипотетическую функцию runit:
package Alpha;
runit('$line = ');
package Beta;
sub runit {
my $codestr = shift;
eval $codestr;
die if $@;
}
Такой подход работает лишь в том случае, если переменная $line является глобальной. Для лексических переменных он не годится. Обходное решение - сделать так, чтобы функция г u nit принимала ссылку на функцию: package Beta; sub runit { my $codestr = shift; my $hispack = caller; eval "package $hispack; $codestr"; die if $@; Новое решение не только работает с лексическими переменными, но и обладает дополнительным преимуществом - синтаксис кода проверяется во время компиляции, а это существенный плюс. При передаче файлового манипулятора стоит воспользоваться более переносимым решением - функцией Symbol: : qualify. Она получает имя и пакет, для которого оно уточняется. Если имя нуждается в уточнении, оно исправляется, а в противном случае остается без и Следующий пример читает и возвращает п строк из файлового манипулятора, Перед тем как работать с манипулятором, функция qualify уточняет его.
open (FH, "< /etc/termcap")
or die "can't open /etc/termcap: $!";
($a, $b, $c) = nreadline(3, 'FH');
use Symbol ();
use Carp;
sub nreadline {
my ($count, $handle) = @_;
my(@retlist,$line);
croak "count must be > 0" unless $count > 0;
$handle = Symbol::qualify($handle, (caller())[OJ);
croak "need open filehandle" unless defined fileno($handle);
push(@"retlist, $line) while defined($line = <$handle>) && $count--;
return Oretlist;
}

Если при вызове функции nreadline файловый манипулятор всегда перелается в виде тип-глоба *FH, ссылки на глоб \*FH или с помощью объектов Filehr die или 10: : Handle, уточнение не потребуется. Оно необходимо лишь на случаи передачи минимального "FH".

> Смотри также -----------------------------
Документация по стандартному модулю Symbol; рецепт 12.12. Специальные метапеременные _FILE_, _LINE_ и _PACKAGE_ описаны в perldala(1),

12.6. Автоматизированное выполнение завершающего кода

Проблема

Требуется создать для модуля начальный и завершающий код, вызываемый автоматически без вмешательства пользователя.

Решение

Начальный код реализуется просто - разместите нужные команды вне определений подпрограмм в файле модуля. Завершающий код помещается в блок END модуля.

Комментарий

В некоторых языках программист должен вызвать инициализирующий код модуля, прежде чем вызывать какие-либо его функции. Аналогично, при завершении программы от программиста может потребоваться вызов завершающего кода, выполняющего деинициализацию модуля. В Perl дело обстоит иначе. Инициализирующий код модуля образуют команды, не входящие ни в одну подпрограмму модуля. Этот код выполняется непосредственно при загрузке модуля. Пользователю никогда не приходится следить за вызовом начального кода, поскольку Для чего нужен автоматический вызов завершающего кода? Все зависит от модуля. Допустим, вам захотелось записать информацию о завершении в системный журнал, приказать серверу базы данных актуализировать все незаконченные операции, обновить состояние экрана Предположим, модуль должен регистрировать начало и завершение своей работы в журнале. Вставьте следующий фрагмент в блок END, чтобы он выполнялся при завершении программы:
$Logfile = "/tmp/mylog" unless defined $Logfile;
open(LF, ""$Logfile")
or die "can't append to $Logfile: $!";
select(((select(LF), $|=1))[0]); # Отменить буферизацию LF
logmsg("startup");
sub logmsg {
my $now = scalar gmtime;
print LF "$0 $$ Snow: @_\n"
or die "write to $Logfile failed: $!";
END {
logmsg("shutdown");
close(LF)
or die "close $Logfile failed: $!
Первая часть кода, не входящая в объявления функций, выполняется во время загрузки модуля. Для этого от пользователя модуля не потребуется никаких специальных действий. Впрочем, для кого-нибудь это может оказаться неприятным сюрпризом, поскольку при недос Блоки END не отличаются от других функций завершения - trap 0 в команд ном интерпретаторе, atexit в языке С или глобальные деструкторы в объектно-ориентированных языках. Порядок выполнения END противоположен порядку загрузки модулей; иначе говоря, первым Однако с неперехваченными сигналами дело обстоит иначе. При завершении по сигналу блоки завершения не вызываются. Проблема решается следующей директивой: use sigtrap qw(die normal-signals error-signals) END также не вызывается в случае, если процесс вызывает функцию ехес, поскольку процесс остается тем же самым, изменяется лишь программа. Все стандартные атрибуты (идентификатор процесса и его родителя, идентификаторы пользователя и группы, маска доступа,

> Смотри также ------------------------------
Стандартная директива use sigtrap описана в perlmod(1), а переменная $"F в perldata(1). Функции fork и ехес рассматриваются в perlmod(1).


© copyright 2000 Soft group
Используются технологии uCoz