Подтвердить что ты не робот

Какова роль блока BEGIN в Perl?

Я знаю, что блок BEGIN компилируется и выполняется перед основным телом программы Perl. Если вы не уверены в этом, просто попробуйте запустить команду perl -cw над этим:

#!/ms/dist/perl5/bin/perl5.8

use strict;
use warnings;

BEGIN {
    print "Hello from the BEGIN block\n";
}

END {
    print "Hello from the END block\n";
}

Мне говорили, что ранняя компиляция и выполнение блока BEGIN позволяет программисту обеспечить доступность любых необходимых ресурсов до того, как будет запущена основная программа.

И поэтому я использовал блоки BEGIN, чтобы убедиться, что такие вещи, как соединения с БД, установлены и доступны для использования основной программой. Аналогично, я использую блоки END, чтобы гарантировать, что все ресурсы будут закрыты, удалены, завершены и т.д. До того, как программа завершится.

После обсуждения сегодня утром мне интересно, не так ли это, чтобы посмотреть блоки BEGIN и END.

Какова предполагаемая роль блока BEGIN в Perl?

Обновление 1: Просто выяснили, почему соединение DBI не работает. После получения этой маленькой программы Perl:

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";

при выполнении он печатает 12.

Обновление 2: Благодаря комментарию Эрика Стром ниже этой новой версии становится понятнее:

use strict;
use warnings;

my $x = 12;
my $y;

BEGIN {
    $x = 14;
    print "x => $x\n";
    $y = 16;
    print "y => $y\n";
}

print "x => $x\n";
print "y => $y\n";

а выход -

x => 14
y => 16
x => 12
y => 16

Еще раз спасибо Eric!

4b9b3361

Ответ 1

Вы пытались выполнить замену блока BEGIN{} для блока INIT{}? Это стандартный подход для таких вещей, как modperl, которые используют модель "compile-once, run-many", поскольку вам нужно инициализировать вещи заново в каждом отдельном прогоне, а не только один раз во время компиляции.

Но я должен спросить, почему все это в специальном блоке в любом случае. Почему бы вам просто не сделать какую-либо функцию prepare_db_connection(), а затем вызвать ее так, как вам нужно, когда программа запустится?

Что-то, что не будет работать в BEGIN{}, также будет иметь ту же проблему, если это основной код в файле модуля, который получает use d. Это еще одна возможная причина использования блока INIT{}.

Я также видел смертоносные проблемы взаимной рекурсии, которые нужно распутать, используя нечто вроде require вместо use или INIT{} вместо BEGIN{}. Но это довольно редко.

Рассмотрим эту программу:

% cat sto-INIT-eg
#!/usr/bin/perl -l
print               "    PRINT: main running";
die                 "    DIE:   main dying\n";
die                 "DIE XXX /* NOTREACHED */";
END         { print "1st END:   done running"    }
CHECK       { print "1st CHECK: done compiling"  }
INIT        { print "1st INIT:  started running" }
END         { print "2nd END:   done running"    }
BEGIN       { print "1st BEGIN: still compiling" }
INIT        { print "2nd INIT:  started running" }
BEGIN       { print "2nd BEGIN: still compiling" }
CHECK       { print "2nd CHECK: done compiling"  }
END         { print "3rd END:   done running"    }

При компиляции только он производит:

% perl -c sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
sto-INIT-eg syntax OK

Пока компилируется и выполняется, он производит это:

% perl sto-INIT-eg 
1st BEGIN: still compiling
2nd BEGIN: still compiling
2nd CHECK: done compiling
1st CHECK: done compiling
1st INIT:  started running
2nd INIT:  started running
    PRINT: main running
    DIE:   main dying
3rd END:   done running
2nd END:   done running
1st END:   done running

И оболочка сообщает о выходе из 255 на die.

Вы должны уметь организовать соединение, когда оно вам понадобится, даже если BEGIN{} окажется слишком ранним.

Хм, просто вспомнил. Нет никакого шанса, что вы что-то делаете с DATA в BEGIN{}, есть ли? Это не настроено до запуска переводчика; он не открывается для компилятора.

Ответ 2

В то время как BEGIN и END блоки могут использоваться, как вы описываете, типичное использование заключается в внесении изменений, влияющих на последующую компиляцию.

Например, оператор use Module qw/a b c/; фактически означает:

BEGIN {
   require Module;
   Module->import(qw/a b c/);
}

аналогично, декларация подпрограммы sub name {...} на самом деле:

BEGIN {
   *name = sub {...};
}

Поскольку эти блоки запускаются во время компиляции, все строки, которые скомпилированы после запуска блока, будут использовать новые определения, которые были сделаны блоками BEGIN. Так вы можете вызывать подпрограммы без круглых скобок или как различные модули "меняют способ работы в мире".

Блоки

END могут использоваться для очистки изменений, выполненных блоками BEGIN, но чаще всего используются объекты с методом DESTROY.

Если состояние, которое вы пытаетесь очистить, является соединением DBI, выполнение этого в блоке END прекрасное. Я бы не создал соединение в блоке BEGIN, хотя по нескольким причинам. Обычно во время компиляции соединение не требуется. Выполнение таких действий, как подключение к базе данных во время компиляции, резко замедлит работу любого редактора, который использует синтаксическую проверку (потому что он работает perl -c).

Ответ 3

В то время как другие ответы верны, мне также стоит упомянуть использование блоков BEGIN и END при использовании переключателей -n или -p для Perl.

Из http://perldoc.perl.org/perlmod.html

Когда вы используете переменные -n и -p для Perl, BEGIN и END работают так же, как и в awk, как вырожденный случай.

Для тех, кто не знаком с коммутатором -n, он сообщает Perl о завершении программы:

while (<>) {
    ...  # your program goes here
}

http://perldoc.perl.org/perlrun.html#Command-Switches, если вас интересует более конкретная информация о коммутаторах Perl.

В качестве примера, демонстрирующего использование BEGIN с помощью переключателя -n, этот однострочный Perl перечисляет строки команды ls:

ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;'

В этом случае BEGIN -block используется для инициализации переменной $i, устанавливая ее в 1 перед обработкой строк ls. В этом примере будет выведено что-то вроде:

1: foo.txt
2: bar.txt
3: program.pl
4: config.xml