【问题标题】:What is the role of the BEGIN block in Perl?Perl 中 BEGIN 块的作用是什么?
【发布时间】:2010-10-22 15:56:05
【问题描述】:

我知道 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 块的错误方式。

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

更新 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

再次感谢埃里克!

【问题讨论】:

  • 如果是这种情况,为什么我不能使用标准 DBI connect() 函数在 BEGIN 块中创建到 DB 的 DBI 连接?如果我删除 BEGIN 块,则建立连接
  • 对于您的更新:这是因为my $x = 12 在编译时声明了变量但不执行赋值。然后开始块运行分配 14。然后当程序开始运行时,my 行的分配运行给你 12。

标签: perl


【解决方案1】:

虽然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)。

【讨论】:

  • 谢谢埃里克。我不确定是否要在关于在非 OO 程序中使用 END 块的问题中添加警告。
【解决方案2】:

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

但我不得不问为什么无论如何它都在特殊块中。为什么不做一些prepare_db_connection() 函数,然后在程序启动时根据需要调用它?

如果模块文件中的主行代码获得used,则在BEGIN{} 中不起作用的东西也会有同样的问题。这是使用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"    }

仅编译时,它会产生:

% 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 做某事,是吗?直到解释器运行才设置好;它不对编译器开放。

【讨论】:

    【解决方案3】:

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

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

    当您在 Perl 中使用 -n 和 -p 开关时,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,在处理ls 的行之前将其设置为1。此示例将输出如下内容:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-10
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 2013-12-06
      • 2017-04-25
      • 2017-08-13
      • 1970-01-01
      相关资源
      最近更新 更多