简体   繁体   English

BEGIN块在Perl中起什么作用?

[英]What is the role of the BEGIN block in Perl?

I know that the BEGIN block is compiled and executed before the main body of a Perl program. 我知道BEGIN块是在Perl程序的主体之前编译和执行的。 If you're not sure of that just try running the command perl -cw over this: 如果不确定这一点,请尝试运行命令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";
}

I have been taught that early compilation and execution of a BEGIN block lets a programmer ensure that any needed resources are available before the main program is executed. 有人告诉我,早期编译和执行BEGIN块可以使程序员确保在执行主程序之前可以使用任何需要的资源。

And so I have been using BEGIN blocks to make sure that things like DB connections have been established and are available for use by the main program. 因此,我一直在使用BEGIN块来确保已经建立了诸如DB连接之类的东西,并且可供主程序使用。 Similarly, I use END blocks to ensure that all resources are closed, deleted, terminated, etc. before the program terminates. 同样,我使用END块来确保在程序终止之前关闭,删除,终止所有资源。

After a discussion this morning, I am wondering if this the wrong way to look at BEGIN and END blocks. 经过今天早上的讨论之后,我想知道这是否是看待BEGIN和END块的错误方法。

What is the intended role of a BEGIN block in Perl? BEGIN块在Perl中的预期作用是什么?

Update 1: Just found out why the DBI connect didn't work. 更新1:刚刚找出了为什么DBI连接不起作用。 After being given this little Perl program: 得到这个小的Perl程序后:

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";

when executed it prints 12. 执行时将打印12。

Update 2: Thanks to Eric Strom's comment below this new version makes it clearer: 更新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";

and the output is 输出是

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

Once again, thanks Eric! 再次感谢Eric!

While BEGIN and END blocks can be used as you describe, the typical usage is to make changes that affect the subsequent compilation. 虽然可以按照您的描述使用BEGINEND块,但是通常的用法是进行会影响后续编译的更改。

For example, the use Module qw/abc/; 例如, use Module qw/abc/; statement actually means: 声明实际上意味着:

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

similarly, the subroutine declaration sub name {...} is actually: 同样,子例程声明sub name {...}实际上是:

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

Since these blocks are run at compile time, all lines that are compiled after a block has run will use the new definitions that the BEGIN blocks made. 由于这些块是在编译时运行的,因此,在块运行后编译的所有行都将使用BEGIN块所做的新定义。 This is how you can call subroutines without parenthesis, or how various modules "change the way the world works". 这是您可以在不带括号的情况下调用子例程的方式,或者各种模块如何“改变世界的工作方式”。

END blocks can be used to clean up changes that the BEGIN blocks have made but it is more common to use objects with a DESTROY method. END块可用于清除BEGIN块所做的更改,但更常见的是将对象与DESTROY方法一起使用。

If the state that you are trying to clean up is a DBI connection, doing that in an END block is fine. 如果您要清除的状态是DBI连接,则可以在END块中执行此操作。 I would not create the connection in a BEGIN block though for several reasons. 尽管出于多种原因,我不会在BEGIN块中创建连接。 Usually there is no need for the connection to be available at compile time. 通常,不需要在编译时使用连接。 Performing actions like connecting to a database at compile time will drastically slow down any editor you use that has syntax checking (because that runs perl -c ). 执行诸如在编译时连接到数据库之类的操作将大大减慢您使用的所有具有语法检查的编辑器的速度(因为该编辑器运行perl -c )。

Have you tried swapping out the BEGIN{} block for an INIT{} block? 您是否尝试过将BEGIN{}块换成INIT{}块? That's the standard approach for things like modperl which use the "compile-once, run-many" model, as you need to initialize things anew on each separate run, not just once during the compile. 这是诸如modperl之类的使用“一次编译,多次运行”模型的标准方法,因为您需要在每次单独运行时重新初始化,而不仅仅是在编译过程中一次。

But I have to ask why it's all in special block anyway. 但是我必须问为什么无论如何都是如此。 Why don't you just make some sort of prepare_db_connection() function, and then call it as you need to when the program starts up? 为什么不制作某种prepare_db_connection()函数,然后在程序启动时根据需要调用它呢?

Something that won't work in a BEGIN{} will also have the same problem if it's main-line code in a module file that gets use d. 如果在BEGIN{}不起作用的某些东西,如果它是use d的模块文件中的主代码,也会遇到同样的问题。 That's another possible reason to use an INIT{} block. 这是使用INIT{}块的另一个可能原因。

I've also seen deadly-embrace problems of mutual recursion that have to be unravelled using something like an require instead of use , or an INIT{} instead of a BEGIN{} . 我还看到了相互递归的致命问题,必须使用require而不是use ,或者使用INIT{}而不是BEGIN{} But that's pretty rare. 但这非常罕见。

Consider this program: 考虑以下程序:

% 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"    }

When compiled only, it produces: 仅编译时,它会产生:

% 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

While when compiled and executed, it produces this: 在编译执行时,它会产生以下结果:

% 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

And the shell reports an exit of 255, per the die . 并且每个die ,shell报告退出值为255。

You should be able to arrange to have the connection happen when you need it to, even if a BEGIN{} proves too early. 即使BEGIN{}证明为时过早,您也应该能够安排在需要时进行连接。

Hm, just remembered. 嗯,只是想起了。 There's no chance you're doing something with DATA in a BEGIN{} , is there? 您不可能对BEGIN{} DATA进行任何操作,是吗? That's not set up till the interpreter runs; 直到解释器运行时才建立; it's not open to the compiler. 它不对编译器开放。

While the other answers are true, I find it also worth to mention the use of BEGIN and END blocks when using the -n or -p switches to Perl. 尽管其他答案是正确的,但我也值得一提的是在将-n-p切换到Perl时使用BEGINEND块。

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

When you use the -n and -p switches to Perl, BEGIN and END work just as they do in awk, as a degenerate case. 当使用-n和-p切换到Perl时,BEGIN和END就像在awk中一样工作,这是简并的情况。

For those unfamiliar with the -n switch, it tells Perl to wrap the program with: 对于那些不熟悉-n开关的人,它告诉Perl用以下命令包装程序:

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

http://perldoc.perl.org/perlrun.html#Command-Switches if you're interested about more specific information about Perl switches. 如果您对有关Perl开关的更多特定信息感兴趣,请http://perldoc.perl.org/perlrun.html#Command-Switches

As an example to demonstrate the use of BEGIN with the -n switch, this Perl one-liner enumerates the lines of the ls command: 作为演示使用-n开关使用BEGIN的示例,此Perl单行代码列举了ls命令的行:

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

In this case, the BEGIN -block is used to initiate the variable $i by setting it to 1 before processing the lines of ls . 在这种情况下, BEGIN块用于在处理ls的行之前通过将其设置为1来初始化变量$i This example will output something like: 此示例将输出类似:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM