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

Rob*_*lls 36 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";
}
Run Code Online (Sandbox Code Playgroud)

我被告知,早期编译和执行BEGIN块可以让程序员确保在执行主程序之前可以获得所需的任何资源.

所以我一直在使用BEGIN块来确保数据库连接已经建立并可供主程序使用.类似地,我使用END块来确保在程序终止之前关闭,删除,终止所有资源等.

经过今天早上的讨论,我想知道这是否是错误的方式来看待BEGIN和END块.

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

更新1:刚刚发现为什么DBI连接不起作用.在给出这个小Perl程序之后:

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";
Run Code Online (Sandbox Code Playgroud)

执行时打印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";
Run Code Online (Sandbox Code Playgroud)

而输出是

x => 14
y => 16
x => 12
y => 16
Run Code Online (Sandbox Code Playgroud)

再次感谢Eric!

Eri*_*rom 31

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

例如,该use Module qw/a b c/;陈述实际上意味着:

BEGIN {
   require Module;
   Module->import(qw/a b c/);
}
Run Code Online (Sandbox Code Playgroud)

同样,子程序声明sub name {...}实际上是:

BEGIN {
   *name = sub {...};
}
Run Code Online (Sandbox Code Playgroud)

由于这些块在编译时运行,因此在块运行之后编译的所有行都将使用BEGIN块所做的新定义.这就是你如何调用没有括号的子程序,或者各种模块如何"改变世界的运作方式".

END块可用于清除BEGIN块所做的更改,但更常见的是使用带有DESTROY方法的对象.

如果您要清理的状态是DBI连接,那么在END块中执行此操作就可以了.BEGIN出于几个原因,我不会在块中创建连接.通常,在编译时不需要连接.执行诸如在编译时连接到数据库的操作将极大地减慢您使用具有语法检查的任何编辑器(因为它运行perl -c).


tch*_*ist 22

你有没有试过换掉BEGIN{}一个INIT{}街区?这是modperl使用"一次编译,多次运行"模型的标准方法,因为你需要在每次单独运行时重新初始化事物,而不是在编译期间重新初始化一次.

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

BEGIN{}如果一个模块文件中的主线代码得到used ,那么在一个遗嘱中无效的东西也会遇到同样的问题.这是使用INIT{}块的另一个可能原因.

我也看到了致命的拥抱相互递归的问题,这些问题必须通过使用类似于require代替useINIT{}代替的东西来解开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"    }
Run Code Online (Sandbox Code Playgroud)

仅编译时,它会产生:

% 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
Run Code Online (Sandbox Code Playgroud)

在编译执行时,它会产生以下结果:

% 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
Run Code Online (Sandbox Code Playgroud)

并且shell报告了一个255的退出die.

您应该可以安排在需要时进行连接,即使BEGIN{}证明太早.

嗯,记得.有没有机会,你正在做的事情有DATA一个BEGIN{},是吗?在翻译运行之前没有设置; 它不对编译器开放.


osc*_*fri 5

尽管其他答案都是正确的,但我还是值得一提的是在使用or 切换到Perl 时使用BEGINand END块。-n-p

来自http://perldoc.perl.org/perlmod.html

当使用-n和-p切换到Perl时,BEGIN和END就像在awk中一样工作,这是简并的情况。

对于那些不熟悉该-n开关的人,它会告诉Perl用以下方法包装程序:

while (<>) {
    ...  # your program goes here
}
Run Code Online (Sandbox Code Playgroud)

如果您对有关Perl开关的更多特定信息感兴趣,请http://perldoc.perl.org/perlrun.html#Command-Switches

作为演示如何BEGIN-n开关中使用的示例,此Perl单行代码列举了ls命令的各行:

ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;'
Run Code Online (Sandbox Code Playgroud)

在这种情况下,通过BEGIN-block可以$i通过在处理行之前将其设置为1 来初始化变量ls。此示例将输出类似:

1: foo.txt
2: bar.txt
3: program.pl
4: config.xml
Run Code Online (Sandbox Code Playgroud)