【问题标题】:Perl - copy variables value out of BEGIN blockPerl - 从 BEGIN 块中复制变量值
【发布时间】:2017-04-25 20:46:57
【问题描述】:

我有一个简单的脚本:

our $height = 40;
our $width = 40;

BEGIN {
    GetOptions( 'help' => \$help,           
                'x=i' => \$width,
                'y=i' => \$height) or die "No args.";


    if($help) { 
        print "Some help";   
        exit 0;
    }

    print $width."\n"; #it is 10 when call with script.pl -x 10 -y 10
    print $height."\n"; #it is 10 when call with script.pl -x 10 -y 10

    #some other code which check installed modules

    eval 'use Term::Size::Any qw( chars pixels )';
    if ( $@ ) {
        if ( $@ =~ /Cant locate (\S+)/ ) {
            warn "No modules";
            exit 2;
        }
    }


}

print $width."\n"; #but here is still 40 not 10
print $height."\n";#but here is still 40 not 10

我用 2 个参数(x 和 y)调用这个脚本,例如:script.pl -x 10 -y 10。但是给定的值没有保存在变量 $width 和 $height 中。我想通过给出参数来改变这个变量。如何复制给定值或将它们保存到$width$height?有可能吗?

已编辑 - 我在此示例中添加了一些代码

【问题讨论】:

    标签: perl copy block


    【解决方案1】:

    BEGIN 子句在普通代码之前执行。当您声明 $height$width 时,您在处理选项之后将它们设置为 40。

    解决方案:处理BEGIN子句之外的选项。

    【讨论】:

    • 我知道。但是如果用户没有安装模块(eval...)并且我想总是显示帮助(-h)怎么办?当我在开始块之后处理选项时,我会一直收到“无模块”消息。当我在开始块中处理选项时,当我通过 -h otpion 时,我将首先收到“一些帮助”消息,而不是“无模块”。是否可以从外部的开始块复制值?
    • 为什么还要在BEGIN 中这样做呢?如果您确实需要有关模块安装的自定义帮助,那么您可以在运行时使用 require 加载模块。
    • 使用require 代替use?
    【解决方案2】:

    所有BEGIN 块都在编译阶段尽快执行(在它们被解析之后)——甚至在运行阶段开始之前。见in perlmodthis "Effective Perler" article。此外,my $x = 1; 的声明部分也发生在编译阶段,但分配是在运行时完成的。

    因此$height$weight 被声明,然后您的处理选项的代码在其BEGIN 块中运行,一旦解释器进入运行阶段然后变量是分配 40,覆盖在该 BEGIN 块中分配的任何内容。

    因此,一种解决方法是仅在 BEGIN 块之前声明这些变量,而不进行赋值,如果变量仍未定义,则在 BEGIN 块之后分配 40 (我认为,作为默认值)。

    但是,最好不要在 BEGIN 块中处理选项或做任何此类大量工作。

    这里有几种其他方法可以满足您的需要。

    只要您在运行时知道它是否有效,就可以在编译期间加载模块。所以像你一样加载它,在 BEGIN 块中的 eval 下,这样你就可以设置一个标志供以后使用(有条件地)。这个标志需要在BEGIN 块之前声明(没有赋值)。

    my $ok_Term_Size_Any;
    
    BEGIN { 
        eval 'use Term::Size::Any qw(chars pixels)';
        $ok_Term_Size_Any = 1 unless $@;
    };
    
    # use the module or else, based on $ok_Term_Size_Any
    

    声明发生在编译时,并且在BEGIN 中,赋值也是如此——在条件if not $@ 下。如果该条件失败(无法加载模块)分配不会发生并且变量保持未定义。因此它可以用作进一步处理的标志。

    另外:虽然没有显示其余代码,但我无法想象需要our;请改用my


    注意 请参阅question 了解有关以下方法的细节

    或者,在运行时加载所有“棘手的”模块。那么解析选项就没有问题了,现在也可以在运行时正常完成,在这些模块之前或之后,视情况而定。

    use Module qw(LIST); 语句正是

    BEGIN {
        require Module;
        Module->import(LIST);
    };
    

    use。所以要在运行时检查一个模块,在你使用它之前,运行并eval该代码

    use warnings 'all';
    use strict;
    
    eval {
        require Module;
        Module->import( qw(fun1 fun2 ...) );
    };
    if ($@) {
        # Load an alternative module or set a flag or exit ...
    };
    
    # use the module or inform the user based on the flag
    

    可以使用Try::Tiny,而不是使用eval(带有必要的错误检查),但请注意,这也存在问题。请参阅this post,也可以讨论有关选择的讨论。使用模块而不是eval-and-$@ 的硬性原因是resolved in 5.14


    【讨论】:

    • 看起来不错,但如果未安装模块,我会收到错误堆栈跟踪。这个想法是,如果未安装模块,我想显示未安装模块并退出的通知消息。但是,如果我使用 -h 选项运行程序,即使未安装模块,我也想显示帮助消息。有可能吗?
    • 在第一个例子中,你确定$ok_Term_Size_Any 会在 BEGIn 块之后设置吗?我认为它将具有默认值,否则会因为未初始化而引发错误。
    • “您的处理选项的代码在分配变量 40 之前运行” 这并不能解释为什么在分配 40 之前执行 BEGIN 块变量。赋值在 BEGIN 块之前并且不在 BEGIN 块本身中?
    • @Borodin 我不明白它是如何解释它的......?选项的处理首先运行(在运行时分配之前),因为它位于BEGIN 块中,而对40 的分配发生在运行阶段,稍后出现。第一句话就确立了这一点。你是说措辞不清楚?
    • @allocer 我不明白。试试这个my $ok_NoMod; BEGIN { eval 'use NoMod'; $ok_NoMod = 1 unless $@ }; if ($ok_NoMod) { say "Have NoMod" } else { say "No 'NoMod' }。添加警告和严格。它说'No NoMod',如果我使用我拥有的模块(而不是这个“NoMod”),那么它说'Have ...'。这就是你需要的,不是吗?
    【解决方案3】:

    问题在于our $height = 40 等声明/定义分两个阶段执行。 声明在编译时执行,而赋值在运行时完成。这意味着类似

    my $x = 0;
    
    BEGIN {
        $x = 1;
    }
    
    say $x;
    

    将显示0,因为$x 在编译时被声明,并且由于BEGIN 块而在编译时设置为1。但在运行时它会被设置为零。

    您只需将声明/定义更改为声明即可。这样,BEGIN 块所做的分配就不会在运行时修改

    use strict;
    use warnings 'all';
    use feature 'say';
    
    my $xx;
    
    BEGIN {
        $xx = 1;
    }
    
    say $xx;
    

    输出

    1
    

    请注意,不需要ourmy 几乎总是首选。并且请不要使用BEGIN 块来执行大量代码:它们应该保留用于准备操作,例如在运行时开始之前加载所需的模块。没有人希望程序在无法编译时输出帮助文本,而这正是您正在尝试做的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-06
      • 2012-03-24
      • 1970-01-01
      相关资源
      最近更新 更多