Search code examples
perlcopyblock

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. I want change this variables by giving arguments. How can I copy given values or save them into $width and $height? Is it possible?

EDITED - I added some code to this example


Solution

  • All BEGIN blocks are executed in the compile phase, as soon as possible (right after they're parsed) -- before the run phase even starts. See this in perlmod and see this "Effective Perler" article. Also, the declaration part of 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.

    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).

    However, it is better not to process options, or do any such extensive work, in a BEGIN block.

    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). This flag need be declared (without assignment) before that BEGIN block.

    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 $@. 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; use my instead.


    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); 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. 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.