简体   繁体   English

Perl - 从 BEGIN 块中复制变量值

[英]Perl - copy variables value out of BEGIN block

I have a simple script:我有一个简单的脚本:

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

I call this script with 2 parameters (x and y), for example: script.pl -x 10 -y 10. But the given values are not saved in variables $width and $height.我用 2 个参数(x 和 y)调用这个脚本,例如:script.pl -x 10 -y 10。但是给定的值没有保存在变量 $width 和 $height 中。 I want change this variables by giving arguments.我想通过给出参数来改变这个变量。 How can I copy given values or save them into $width and $height ?如何复制给定值或将它们保存到$width$height Is it possible?是否可以?

EDITED - I added some code to this example编辑- 我在这个例子中添加了一些代码

The BEGIN clause is executed before normal code. BEGIN子句正常代码之前执行。 When you declare $height and $width , you set them to 40 after you process the options.声明$height$width处理选项后将它们设置为 40。

Solution: process the options outside the BEGIN clause.解决方案:处理BEGIN子句之外的选项。

The problem is that declaration/definitions like our $height = 40 etc. are executed in two phases.问题是像our $height = 40等声明/定义是分两个阶段执行的。 The declaration is performed at compilation time, while the assignment is done at run time.声明在编译时执行,而赋值在运行时完成。 That means something like这意味着类似

my $x = 0;

BEGIN {
    $x = 1;
}

say $x;

will display 0 , because $x is declared at compile time and set to 1 at compile time because of the BEGIN block.将显示0 ,因为$x在编译时声明并在编译时由于BEGIN块而设置为 1 。 But it is then set to zero at run time.但它在运行时被设置为零。

All you need to do it change the declaration/definition to just a declaration.您只需将声明/定义更改为声明即可。 That way there is no run-time modification of the assignment made by the BEGIN block这样就不会在运行时修改BEGIN块所做的分配

use strict;
use warnings 'all';
use feature 'say';

my $xx;

BEGIN {
    $xx = 1;
}

say $xx;

output输出

1

Note that there is no need for our .请注意,不需要our . my is almost always preferable. my的几乎总是可取的。 And please don't use BEGIN blocks to execute significant chunks of code: they should be reserved for preparatory actions of comparable to loading the required modules before run time starts.并且请不要使用BEGIN块来执行重要的代码块:它们应该保留用于类似于在运行时开始之前加载所需模块的准备操作。 No one expects a program to output help text if it won't compile, which is what you are trying to do.没有人希望程序在无法编译时输出帮助文本,而这正是您想要做的。

All BEGIN blocks are executed in the compile phase , as soon as possible (right after they're parsed) -- before the run phase even starts.所有BEGIN块都在编译阶段执行,尽快(在它们被解析之后)——甚至在运行阶段开始之前。 See this in perlmod and see this "Effective Perler" article . 在 perlmod 中查看此内容并查看这篇“Effective Perler”文章 Also, the declaration part of my $x = 1;另外, my $x = 1;的声明部分my $x = 1; happens in the compile phase as well, but the assignment is done at runtime.也发生在编译阶段,但分配是在运行时完成的。

Thus the $height and $weight are declared, then your code to process the options runs in its BEGIN block, and once the interpreter gets to the run phase then the variables are assigned 40 , overwriting whatever had been assigned in that BEGIN block.因此声明了$height$weight ,然后处理选项的代码在其BEGIN块中运行,一旦解释器进入运行阶段,变量被分配40 ,覆盖在该BEGIN块中分配的任何内容。

Thus a way around that is to only declare these variables, without assignment, before the BEGIN block, and assign that 40 after the BEGIN block if the variables are still undefined (I presume, as default values).因此,一种解决方法是BEGIN块之前声明这些变量,而无需赋值,如果变量仍未定义(我假设为默认值),则在BEGIN块之后分配40

However, it is better not to process options, or do any such extensive work, in a BEGIN block.但是,最好不要在BEGIN块中处理选项或进行任何此类大量工作。

Here are a couple of other ways to do what you need.这里有一些其他方法可以满足您的需求。

Loading the module during compilation is fine for your purpose, as long as you know at runtime whether it worked.在编译期间加载模块很适合您的目的,只要您在运行时知道它是否有效。 So load it as you do, under eval in a BEGIN block, so that you can set a flag for later use (conditionally).因此,在BEGIN块中的eval下按您的方式加载它,以便您可以设置一个标志供以后使用(有条件地)。 This flag need be declared (without assignment) before that BEGIN block.这个标志需要在那个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

The declaration happens at compile time, and being in BEGIN so does the assignment -- under the conditional if not $@ .声明发生在编译时,并且在BEGIN也是如此 - 在条件if not $@ If that condition fails (the module couldn't be loaded) the assignment doesn't happen and the variable stays undefined.如果该条件失败(无法加载模块),则赋值不会发生并且变量保持未定义状态。 Thus it can be used as a flag in further processing.因此,它可以用作进一步处理中的标志。

Also: while the rest of the code isn't shown I can't imagine a need for our ;另外:虽然没有显示其余的代码,但我无法想象需要our ; use my instead.my代替。


NOTE Please consult this question for subtleties regarding the following approach注意请参阅此问题以了解有关以下方法的细微之处

Alternatively, load all "tricky" modules at runtime.或者,在运行时加载所有“棘手”模块。 Then there are no issues with parsing options, what can now be done normally at runtime as well, before or after those modules, as suitable.然后解析选项没有问题,现在可以在运行时正常完成,在这些模块之前或之后,视情况而定。

The use Module qw(LIST); use Module qw(LIST); statement is exactly声明正是

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

See use .请参阅使用 So to check for a module at runtime, before you'd use it, run and eval that code因此,要在运行时检查模块,在使用它之前,请运行并评估该代码

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

Instead of using eval (with the requisite error checking), one can use Try::Tiny but note that there are issues with that, too.可以使用Try::Tiny而不是使用eval (带有必要的错误检查),但请注意,这也存在问题。 See this post , also for a discussion about the choice.请参阅此帖子,也可了解有关选择的讨论。 The hard reasons for using a module instead of eval -and- $@ have been resolved in 5.14 .使用模块而不是eval -and- $@的硬性原因已 在 5.14 中解决


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

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