繁体   English   中英

BEGIN块在Perl中起什么作用?

[英]What is the role of the BEGIN block in 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块来确保已经建立了诸如DB连接之类的东西,并且可供主程序使用。 同样,我使用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!

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

例如, use Module qw/abc/; 声明实际上意味着:

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

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

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

由于这些块是在编译时运行的,因此,在块运行后编译的所有行都将使用BEGIN块所做的新定义。 这是您可以在不带括号的情况下调用子例程的方式,或者各种模块如何“改变世界的工作方式”。

END块可用于清除BEGIN块所做的更改,但更常见的是将对象与DESTROY方法一起使用。

如果您要清除的状态是DBI连接,则可以在END块中执行此操作。 尽管出于多种原因,我不会在BEGIN块中创建连接。 通常,不需要在编译时使用连接。 执行诸如在编译时连接到数据库之类的操作将大大减慢您使用的所有具有语法检查的编辑器的速度(因为该编辑器运行perl -c )。

您是否尝试过将BEGIN{}块换成INIT{}块? 这是诸如modperl之类的使用“一次编译,多次运行”模型的标准方法,因为您需要在每次单独运行时重新初始化,而不仅仅是在编译过程中一次。

但是我必须问为什么无论如何都是如此。 为什么不制作某种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

并且每个die ,shell报告退出值为255。

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

嗯,只是想起了。 您不可能对BEGIN{} DATA进行任何操作,是吗? 直到解释器运行时才建立; 它不对编译器开放。

尽管其他答案是正确的,但我也值得一提的是在将-n-p切换到Perl时使用BEGINEND块。

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

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

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

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

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

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

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

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

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