Any way to access global variable in Perl script from one module file?


M

Michael Yang

I want to access one global variable saved in the main script from
another one module. Here is what it looks like:

A.pl: the main program, with one variable, e.g., $var, declared as
global.
#!/usr/bin/perl
use strict;
use warnings;
use B;
our $var = "hello";
testB();

B.pm: the module file, from which I want to access the $var variable
in A.pl
#!/usr/bin/perl
package B;
use strict;
use warnings;
require "A.pl"; ###########Actually I don't like this way, due
to some problem
sub testB{
print "In B.pm, the variable is $var\n";
############Here I want the $var value in A.pl be accessible.
}

Is there a way doing this like in C language, we just need to declare
it in B.pm, that the $var has been defined elsewhere, so it won't be
checked during compile time but in runtime.

The reason I don't want use 'require' statement, is the A.pl will be
evaluated. But I only need it to check the declaration and assignment
of the variable $var.

Thanks all.
 
Ad

Advertisements

J

Jens Thoms Toerring

Michael Yang said:
I want to access one global variable saved in the main script from
another one module. Here is what it looks like:
A.pl: the main program, with one variable, e.g., $var, declared as
global.
#!/usr/bin/perl
use strict;
use warnings;
use B;
our $var = "hello";
testB();
B.pm: the module file, from which I want to access the $var variable
in A.pl
#!/usr/bin/perl
package B;
use strict;
use warnings;
require "A.pl"; ###########Actually I don't like this way, due
to some problem
sub testB{
print "In B.pm, the variable is $var\n";
############Here I want the $var value in A.pl be accessible.
}
Is there a way doing this like in C language, we just need to declare
it in B.pm, that the $var has been defined elsewhere, so it won't be
checked during compile time but in runtime.

If you have A.pl

#!/usr/bin/perl
use strict;
use warnings;
use Bxx;

our $var = "hello";
Bxx::testB();

and Bxx.pm (I changed the name since there's already a packet called B)

#!/usr/bin/perl
package Bxx;
use strict;
use warnings;

sub testB{
print "In B.pm, the variable is $main::var\n";
}

1;

it simply works;-) The 'main::' if front of the variable name
tells that it's defined in the main package. If 'A' would be
another package instead you would need 'use A;' at the start
and would have to qualify '$var' with 'A::' in front of it,
i.e. as '$A::var'.
Regards, Jens
 
M

Michael Yang

If you have A.pl

#!/usr/bin/perl
use strict;
use warnings;
use Bxx;

our $var = "hello";
Bxx::testB();

and Bxx.pm (I changed the name since there's already a packet called B)

#!/usr/bin/perl
package Bxx;
use strict;
use warnings;

sub testB{
print "In B.pm, the variable is $main::var\n";

}

1;

it simply works;-) The 'main::' if front of the variable name
tells that it's defined in the main package. If 'A' would be
another package instead you would need 'use A;' at the start
and would have to qualify '$var' with 'A::' in front of it,
i.e. as '$A::var'.
Regards, Jens

Thanks for your information.It really helps.
But does it work with indirect invoking?
For example:
If I have one A.pl same as the one you gave, but in A.pl:
I invoked another script "C.pl", in which the testB is called.
A.pl:
#!/usr/bin/perl
use strict;
use warnings;
our $var = "hello";
my @output = `perl C.pl`;

C.pl:
#!/usr/bin/perl
use strict;
use warnings;
use Bxx;
Bxx::testB();

In this way, the $main:: refers to the script invoking the module's
functions, that is C.pl here rather than A.pl.
Is it due to the sub-process?

Thanks.
 
J

Jens Thoms Toerring

Michael Yang said:
But does it work with indirect invoking?
For example:
If I have one A.pl same as the one you gave, but in A.pl:
I invoked another script "C.pl", in which the testB is called.
A.pl:
#!/usr/bin/perl
use strict;
use warnings;
our $var = "hello";
my @output = `perl C.pl`;
C.pl:
#!/usr/bin/perl
use strict;
use warnings;
use Bxx;
Bxx::testB();
In this way, the $main:: refers to the script invoking the module's
functions, that is C.pl here rather than A.pl.
Is it due to the sub-process?

This won't work because from A.pl you start a completely new
child process that knows nothing about any variables set in
the parent process that spawned it. In the example with the
original A.pl and Bxx.pm you had a single process and the
separation into A.pl and Bxx.pm is only something on source
code level, when the script get started, everything gets put
together. But if you start a program via backticks the ope-
rating system creates a process with its own memory and that
has no access to the memory of the parent process. Thus in
this case you somehow have to pass the value of '$var' to
the new process, the simplest method probably being via
command line arguments.
Regards, Jens
 
B

Brian McCauley

Thanks for your information.It really helps.
But does it work with indirect invoking?
For example:
If I have one A.pl same as the one you gave, but in A.pl:
I invoked another script "C.pl", in which the testB is called.
A.pl:
#!/usr/bin/perl
use strict;
use warnings;
our $var = "hello";
my @output = `perl C.pl`;

C.pl:
#!/usr/bin/perl
use strict;
use warnings;
use Bxx;
Bxx::testB();

In this way, the $main:: refers to the script invoking the module's
functions, that is C.pl here rather than A.pl.
Is it due to the sub-process?

Yes, a separate process has a completely separate memory space. This
is exactly the same as if one executable written in C called another -
they wouldn't share variables either.

There is a special variable %ENV that can carry information from
parent to child but not the other way.

I suspect you have an XY problem here. Can you explain what you are
trying to achieve? There is probably no reason to create a new perl
instance. Unless A.pl is a test harness for C.pl then C.pl should
probably just be another module.
 
M

Michael Yang

Yes, a separate process has a completely separate memory space. This
is exactly the same as if one executable written in C called another -
they wouldn't share variables either.

There is a special variable %ENV that can carry information from
parent to child but not the other way.

I suspect you have an XY problem here. Can you explain what you are
trying to achieve? There is probably no reason to create a new perl
instance. Unless A.pl is a test harness for C.pl then C.pl should
probably just be another module.- Hide quoted text -

- Show quoted text -

Thank you all!
I am designing a Test Harness for automation testing purpose.
The A.pl I mentioned is the entrance script of the harness, I call it
as main.pl here.
In main.pl, I need to call the invocant that is processing the jobs of
executing test cases, I call it Testrunner.pl here.
So it should be like this:

main.pl --------> Testrunner.pl ---------|---------------------
($handler)
|---------------------->case2.pm

|---------------------->case3.pm

The $handler is a reference variable saved in the main.pl. I want it
be accessible from each case module, case1.pm, case2.pm, without
interference of Testrunner.pl. Because Testrunner.pl has been
finalized as the agent of running test jobs. I have to keep it
untouched.
What I only could do now is to modify the main.pl and case module
file. That's why I want to define the $handle as a global variable,
and want it be accessed from the module file.
How can I call Testrunner.pl from main.pl without spawning a separate
child process? If it is a possibility, in this way, the solution Jens
gave will work by $main::handler.

Thanks
Michael
 
Ad

Advertisements

M

Michael Yang

main.pl --------> Testrunner.pl ---------|--------------------->case1.pm

($handler)
|---------------------->case2.pm

|---------------------->case3.pm

sorry for the ambiguity of this graph.

main.pl( $handler as its global variable) -------> Testrunner.pl -----
(case1.pm, case2.pm, case3.pm)

Hope this would be clearer for understanding.
 
B

Brian McCauley

I am designing a Test Harness for automation testing purpose.
The A.pl I mentioned is the entrance script of the harness, I call it
as main.pl here.
In main.pl, I need to call the invocant that is processing the jobs of
executing test cases, I call it Testrunner.pl here.
The $handler is a reference variable saved in the main.pl. I want it
be accessible from each case module, case1.pm, case2.pm, without
interference of Testrunner.pl. Because Testrunner.pl has been
finalized as the agent of running test jobs. I have to keep it
untouched.
What I only could do now is to modify the main.pl and case module
file. That's why I want to define the $handle as a global variable,
and want it be accessed from the module file.

The way you are using the term "global variable" is not the way it is
usually used in most languages. (You haven't been using MUMPS have
you?) Usually a global variable is only global within a given process.
How can I call Testrunner.pl from main.pl without spawning a separate
child process?

You could do() it. However this would possibly give odd effects if you
try to call Testrunner.pl more than once from main.pl. I say /
possibly/ because if Testrunner.pl has written following best
practices then it would probably run OK (and all you'd need to do is
suppress the subroutine redefined warning).
If it is a possibility, in this way, the solution Jens
gave will work by $main::handler.

Actually if Testrunner.pl already "thinks" that it "owns" main:: then
you should use another namespace.
 
M

Michael Yang

The way you are using the term "global variable" is not the way it is
usually used in most languages. (You haven't been using MUMPS have
you?) Usually a global variable is only global within a given process.


You could do() it. However this would possibly give odd effects if you
try to call Testrunner.pl more than once from main.pl. I say /
possibly/ because if Testrunner.pl has written following best
practices then it would probably run OK (and all you'd need to do is
suppress the subroutine redefined warning).


Actually if Testrunner.pl already "thinks" that it "owns" main:: then
you should use another namespace.

I am thinking about using 'require' to do this:
@ARGV = ();
require 'Testrunner.pl';

In this way, the variable defined in the main script is in the same
context as the Testrunner.pl, and could be accessed from the module
file.
But I found it slows the harness greatly, the performance is lowered.
How to leaverage on this?
 
B

Brian McCauley

I am thinking about using 'require' to do this:
@ARGV = ();
require 'Testrunner.pl';

In this way, the variable defined in the main script is in the same
context as the Testrunner.pl, and could be accessed from the module
file.

Yes that's what I said. Well I said do() not require(). But the only
difference in this conext is that main.pl _cannot_ run Testrunner.pl
more than once.
But I found it slows the harness greatly, the performance is lowered.

That seems highly counter-intuative. I would expect it to be a little
faster as you are only loading perl once.

How are you observing this? Is it possible that it's not the tests
that are running slowly but rather something like an output buffering
issue?
How to leaverage on this?

Well the bunderbus thing would be to make a copy of the whole thing,
main.pl, Testrunner.pl and at least one test case and check you still
see the difference. Then cut bits out progressively checking at each
stage that you still see the difference.

Eventually you'll have a minimal but complete test case to illustrate
the behaviour. If but this time you've not spotted what's happening
then you should post the three files (which by now should be <20 lines
each).
 
M

Michael Yang

Yes that's what I said. Well I said do() not require(). But the only
difference in this conext is that main.pl _cannot_ run Testrunner.pl
more than once.


That seems highly counter-intuative. I would expect it to be a little
faster as you are only loading perl once.

How are you observing this? Is it possible that it's not the tests
that are running slowly but rather something like an output buffering
issue?


Well the bunderbus thing would be to make a copy of the whole thing,
main.pl, Testrunner.pl and at least one test case and check you still
see the difference. Then cut bits out progressively checking at each
stage that you still see the difference.

Eventually you'll have a minimal but complete test case to illustrate
the behaviour. If but this time you've not spotted what's happening
then you should post the three files (which by now should be <20 lines
each).

Thanks for your pointing it out, Brian.
There is indeed the output buffering codes in the script, but I
thought it works fine.
Since it's a harness containing serveral large scripts, that I can't
paste each piece of the codes, so I only put the key parts of the
codes to simply illustrate what I am doing. Also the codes that might
have relationship with output buffering.

### my main.pl script ####
### <codes omitted>####
## Start calling the Testrunner.pl ###
@ARGV = ( "$testcase", ###The file with list of testcases in xml
format
"$paramFile", ###The file containing the
configuration data
"$logfile"); ###The file that log data is
written to.

do "$<path to runner>/Testrunner.pl";
if ([email protected]){
#### codes to deal with errors ####
}
###<codes omitted>####
### End of main.pl script ###

#### Testrunner.pl script #######
### <codes to process parameters passed by main.pl> omitted###
### Set the autoflush to ON ####
$| = 1;
### <codes to process list of testcases> omitted###
### Start to run the testcase subroutines in each module####
foreach my $sr (@routines)
{
select STDOUT; #route print statements to STDOUT
$logHandler->write($sr) # write the testcase info into logfile,
defined in another module
print "\n\t Executing => $sr";

select $logFileHandle; # route print statements to the logfile,
the one passed into by main.pl

eval
{
no strict "refs";
($sr) -> ($params); #execution of the subroutines contained
in each testcase module
}
}
#### <codes dealing with the rest> omitted ####
#### End of Testrunner.pl #####

### The module for one testcases, case1.pm#####
package case1;
#### <codes dealing with the member variables> omitted ####
sub test1
{
####<codes omitted> ####
###This will be directed to the logfile specified by the "select
$logFileHandle" in Testrunner.pl#####
print "Executing the test1...\n";
$output = `...`; ## There is one child process spawned here, I
saved the output into one variable.
### <rest of codes> omitted ####
}
### <codes left> omitted ####
1;
#####End of case1.pm #######

I just looked throught, and think these parts might have something
with output buffering issue if there is any.
These work fine and fast by invoking Testrunner.pl by spawning a child
process, as discussed previously, except for the problem that the
variable in main.pl can not be accessed from case1.pm.

I am not sure which will affect and cause the buffering issues. If you
have any simple example, could you show me one that might block the I/
O that I could look into it?

Thanks so much!
 
Ad

Advertisements

B

Brian McCauley

[Micheal - please don't put all your comments at the end of the quoted
material. I've tried to intersperse your responses in context. ]

Thanks for your pointing it out, Brian.

Is a single run of main.pl do()ing Testrunner.pl more than once?

As I mentioned before this could give odd effects. Since you are
getting odd effects maybe this is the reason.

This is the key question. You need to be sure if it's really running
slowly.
Since it's a harness containing serveral large scripts, that I can't
paste each piece of the codes, so I only put the key parts of the
codes to simply illustrate what I am doing. Also the codes that might
have relationship with output buffering.

Output buffering issues are unlikely to be responsible for a real drop
in performance so before you start looking for buffering issues you
need to be sure the apparent loss of performance is illusory. If the
loss of performance is real then you should look elsewhere for the
cause.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top