BEGIN块在Perl中的作用是什么?

时间:2010-10-22 15:56:06

标签: 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:感谢Eric Strom在下面的评论,这个新版本更加清晰:

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!

3 个答案:

答案 0 :(得分:31)

虽然可以按照您的描述使用BEGINEND块,但典型用法是进行影响后续编译的更改。

例如,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)。

答案 1 :(得分:22)

您是否尝试过换BEGIN{}阻止INIT{}阻止?这是modperl使用“一次编译,运行多次”模型的标准方法,因为你需要在每次单独运行时重新初始化事物,而不是在编译期间重新初始化一次。

但我不得不问为什么它总是在特殊区块中。为什么不只是制作某种prepare_db_connection()函数,然后在程序启动时调用它?

如果模块文件中的主线代码获得BEGIN{} d,那么在use中无效的东西也会出现同样的问题。这是使用INIT{}块的另一个可能原因。

我也看到了致命的拥抱相互递归的问题,必须使用require代替useINIT{}代替{{1}来解开}}。但这种情况非常罕见。

考虑这个程序:

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 ,shell报告退出255。

即使die过早证明,您也应该能够安排在需要时进行连接。

嗯,只记得。 BEGIN{} DATA中你BEGIN{}无法做某事,是吗?在翻译运行之前没有设置;它不对编译器开放。

答案 2 :(得分:5)

虽然其他答案都是正确的,但我发现在使用BEGINEND切换到Perl时,使用-n-p块也值得一提。

来自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 - 块用于在处理$i行之前将变量设置为1来启动变量ls。此示例将输出如下内容:

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