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