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
The BEGIN
clause is executed before normal code. When you declare $height
and $width
, you set them to 40 after you process the options.
Solution: process the options outside the BEGIN
clause.
The problem is that declaration/definitions like our $height = 40
etc. are executed in two phases. 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. 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
use strict;
use warnings 'all';
use feature 'say';
my $xx;
BEGIN {
$xx = 1;
}
say $xx;
1
Note that there is no need for our
. my
is almost always preferable. 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. 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. 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 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.