accessing arrays in a different sub-routine

E

erik

I tried to find the answer on the web, camel book, and did not know
which perldoc to look in(perlop did not have it). I also tried a few
different things to make it work, any nothing has worked.

I dynamically create an array from an expect output. I want to then
print it out in a different array. I research references private vs.
global scalars and did not find my answer. This is my code:

#!/usr/bin/perl
#################################################################
# Global Variables #
#################################################################
use warnings;
use Expect;
use CGI(":standard");
$Expect::Log_Stdout = 0;
$device = param("device");
$username = param("username");
$password = param("password");
$enable = "enable";
$save = "wr mem";
$enable_password = param("enable_password");
$service_type = param("service_type");
my $datetemp = `date +%x_%r`;
my @dateStamp = split('\n',$datetemp);
my @bandwidth_array;
my $bandwidth_array;
(SNIP)
##################################################################
# Now we check for bandwidth statement #
###################################################################
sub bandwidth_router_check{
$timeout ="15";
my $bandwidth = "The router has at least 1 bandwidth statement";
my $no_bandwidth = "The router is missing a bandwidth statement";
$command->clear_accum();
print $command "show config \| inc band\r";
unless ($command->expect($timeout, -re, '#')) {
return "Never got telnet prompt".$command->exp_error()."\n";
}
my $bandwidth_capture = $command->exp_before();
my @bandwidth_array = split ('\n', $bandwidth_capture);

#HERE WE CHECK LOGGING

if ($bandwidth_capture =~ /band.*/)
{
push (@band_tests, $bandwidth);
$band_action = "OK";
}
else{
push (@band_tests, $no_bandwidth);
$band_action = "FAIL";
}

print "bandwidth_array 1 is $bandwidth_array[1]\n"; #THIS PRINT WORKS
GREAT!

};#end sub


Then I call it in a different sub-routine.

################################################################### #
Prints the main parameters that should be in ALL routers #
###################################################################
sub print_main_report
{
my @bandwidth_array;
my $bandwidth_array;

print "Content-type: text/html\n\n";
print "<html><head><title>QA REPORT</title></head>";
print "<body bgcolor=#000000>";
print "<p align=center><b><font color=#FFFFFF size=5>ROUTER QA
REPORT</font></b></p>";
print "<br>";
print "<p align=center><font color=#FFFFFF><b>Date: $dateStamp[0]
EST<b></font></p>";
print "<br>";
print "<body><font color=#FFFFFF><b>QA Report for:
$device<b></font></body>";
print "<br>";

print "<br>";
#we print for bandwidth
if ($band_action eq "OK")
{
print "<body><font
color=33CC33>@band_tests.............$band_action</font></body>";
}
else{
print "<body><font
color=FF0000>@band_tests.............$band_action</font></body>";
}
print "<br>";
print "<body>bandwidth_array 1 is $bandwidth_array[1]</body>";
#THIS DOES NOT PRINT AN ELEMENT, IT PRINT AN EMPTY ELEMENT.
print "<br>";
}

Can anyone give me some input? I tried to may the refences to it global
instead of scoped, but that did not do it. How should this sort of
thing be done?
 
E

erik

I dynamically create an array from an expect output. I want to then
print it out in a different array. I research references private vs.
global scalars and did not find my answer. This is my code:

I had a typo here, should read:

I dynamically create an array from an expect output. I want to then
print it out in a different sub-routine. I research references private
vs.
global scalars and did not find my answer. This is my code:
 
P

Paul Lalli

erik said:
I tried to find the answer on the web, camel book, and did not know
which perldoc to look in(perlop did not have it). I also tried a few
different things to make it work, any nothing has worked.

I dynamically create an array from an expect output. I want to then
print it out in a different array.

This is non-sensical. Arrays do not print anything.
I research references private vs.
global scalars and did not find my answer.

I don't think you researched enough.
This is my code:
#!/usr/bin/perl
#################################################################
# Global Variables #
#################################################################
use warnings;

You are forgetting "use strict;"
use Expect;
use CGI(":standard");
$Expect::Log_Stdout = 0;
$device = param("device");
$username = param("username");
$password = param("password");
$enable = "enable";
$save = "wr mem";
$enable_password = param("enable_password");
$service_type = param("service_type");

There is no reason to make any of these package variables. They should
all be declared with 'my'
my $datetemp = `date +%x_%r`;
my @dateStamp = split('\n',$datetemp);
my @bandwidth_array;

This variable, having been declared lexical outside of any subroutine,
will be accessable anywhere below its declaration.
my $bandwidth_array;

That's a remarkably poor choice of a variable name. For one, you
already have an array named "bandwidth_array". (Just because Perl
allows this doesn't make it a good idea). For two, this isn't an array,
it's a scalar.

(SNIP)
##################################################################
# Now we check for bandwidth statement #
###################################################################
sub bandwidth_router_check{
$timeout ="15";
my $bandwidth = "The router has at least 1 bandwidth statement";
my $no_bandwidth = "The router is missing a bandwidth statement";
$command->clear_accum();
print $command "show config \| inc band\r";
unless ($command->expect($timeout, -re, '#')) {
return "Never got telnet prompt".$command->exp_error()."\n";
}
my $bandwidth_capture = $command->exp_before();
my @bandwidth_array = split ('\n', $bandwidth_capture);

You are declaring a new lexical array. This array will be valid for the
duration of this subroutine, and will then disappear. For the remainder
of this subroutine, this new variable masks the existing lexical you
decalred above.
#HERE WE CHECK LOGGING

if ($bandwidth_capture =~ /band.*/)
{
push (@band_tests, $bandwidth);
$band_action = "OK";
}
else{
push (@band_tests, $no_bandwidth);
$band_action = "FAIL";
}

print "bandwidth_array 1 is $bandwidth_array[1]\n"; #THIS PRINT WORKS
GREAT!

Of course it does. You're printing the second element of the variable
you just declared in this subroutine.
};#end sub


Then I call it in a different sub-routine.

################################################################### #
Prints the main parameters that should be in ALL routers #
###################################################################
sub print_main_report
{
my @bandwidth_array;

Now here's another entirely new lexical. This one again masks the first
one you declared, and is valid for the duration of this subroutine. It
has absolutely nothing whatsoever to do with either the variable you
declared in the previous subroutine, or with the variable you declared
at the top of the file.
my $bandwidth_array;

print "<body>bandwidth_array 1 is $bandwidth_array[1]</body>";
#THIS DOES NOT PRINT AN ELEMENT, IT PRINT AN EMPTY ELEMENT.

Of course it does. You're trying to print the second element of an array
you never added anything to.
print "<br>";
}

Can anyone give me some input? I tried to may the refences to it global
instead of scoped, but that did not do it. How should this sort of
thing be done?

You generally have two options:
1) Make a variable that is lexically scoped to the entire file. You put
one declaration near the top of the file, just as you did here. You
then simply *use* that variable. Don't declare any more variables of
the same name.
2) Have the function that first sets the variable declare the variable
lexically scoped to the function itself, and then pass that variable (or
at least its value) around using array parameters and function return
values.


You should probably read "Coping with Scoping", which talks about the
different scopes of variables.
http://perl.plover.com/FAQs/Namespaces.html


Paul Lalli
 
A

A. Sinan Unur

my $datetemp = `date +%x_%r`;

In addition to Paul Lalli's excellent commnents elsethread, let me please
ask, WHY?

No need to spawn an external program to just a get a timestamp.

perldoc -f time
perldoc -f localtime
my @dateStamp = split('\n',$datetemp);

perldoc -f chomp

See also the strftime function in the POSIX module.

The only reason you declare and use @dateStamp is to get rid of the EOL at
the end of $datetemp. You might think the above is harmless, but puzzling
gems like this make your programs hard to read later.

Sinan
 
E

erik

I just read that, thanks. That sheds some light. So basically with my
example above, and due to it's dynamic nature, the only way I can get
this working is a sub-function within a subfunction. I really didn't
want to do that, but I take it I have no choice. I thought I could do
my $bandwidth_array; in my printing sub-routine but like you said, it
in't DOING anything.

I liked this line from the coping with scope.

When to Use my and When to Use local
Always use my; never use local.

Wasn't that easy?

BTW, what was wrong with the name that I gave my array
@bandwidth_array? You lost me on that one.
 
A

A. Sinan Unur

I just read that, thanks.

Please quote an appropriate amount of context when replying.
That sheds some light. So basically with my
example above, and due to it's dynamic nature, the only way I can get
this working is a sub-function within a subfunction. I really didn't
want to do that, but I take it I have no choice.

You lost me here.

Here is a simple example of what you can do:

#! /usr/bin/perl

my $files = read_files_in_current_dir();

$files or die "No files in current directory\n";

my_print($files);

sub read_files_in_current_dir {
opendir my $dir, '.'
or die "Cannot open current directory: $!";
[ grep { -f } readdir $dir ];
}

sub my_print {
print "$_\n" for sort @{ shift() };
}

__END__

The first subroutine you call generates the array you want, and returns
a reference to it. Then, you pass that reference to other subroutines
that work on the array.

No need for nested subroutines.
BTW, what was wrong with the name that I gave my array
@bandwidth_array? You lost me on that one.

For me, the '@' in front of the name already signifies that the variable
is an array, why replicate that information?

Sinan
 
A

Ala Qumsieh

A. Sinan Unur said:
@g14g2000cwa.googlegroups.com:


For me, the '@' in front of the name already signifies that the variable
is an array, why replicate that information?

Actually, Paul was commenting on this:

This is a scalar, not an array (note the leading '$' sigil). Having the
name of a scalar end in '_array' can lead to confusion.

--Ala
 
P

Paul Lalli

I wrote, but erik didn't quote:
You generally have two options:
1) Make a variable that is lexically scoped to the entire file. You
put one declaration near the top of the file, just as you did here.
You then simply *use* that variable. Don't declare any more variables
of the same name.
2) Have the function that first sets the variable declare the variable
lexically scoped to the function itself, and then pass that variable
(or at least its value) around using array parameters and function
return values.
I just read that, thanks. That sheds some light. So basically with my
example above, and due to it's dynamic nature, the only way I can get
this working is a sub-function within a subfunction. I really didn't
want to do that, but I take it I have no choice.

How does anything I said above translate to this? I said nothing about
nested subroutines. I simply said to not re-declare your variable
within the subroutine. Just use the variable you already have:

my $foo;
sub fctn1{
$foo = 1;
}
sub fctn2{
print "Foo = $foo\n";
}

fctn1(); #$foo gets set here
fctn2(); #$foo gets printed here.

Your problem is that you had additional "my $foo;" statements within
fctn1 and fctn2. Get rid of them.
BTW, what was wrong with the name that I gave my array
@bandwidth_array? You lost me on that one.

Well, if you'd bother quoting what was said:
my $bandwidth_array; I replied:
That's a remarkably poor choice of a variable name. For one, you
already have an array named "bandwidth_array". (Just because Perl
allows this doesn't make it a good idea). For two, this isn't an
array, it's a scalar.

you'd see that I did not say @bandwidth_array is a poor name. I said
that $bandwidth_array is a poor name. Those are two wholly unrelated
variables. They have nothing to do with one another. Naming the same
name implies that they do. Plus, naming a scalar variable
"something_array" is just a bad idea, I hope for obvious reasons.

Out of curiousity, have you read the posting guidelines for this group yet?

Paul Lalli
 
E

erik

No I haven't read the guidelines. I just looked and did not see them.
Would you mind posting the link and I will be glad to play nice in the
sandbox.
 
A

A. Sinan Unur

No I haven't read the guidelines. I just looked and did not see them.

They are posted here regularly. In fact, a fresh copy was posted today.
Would you mind posting the link and I will be glad to play nice in the
sandbox.

Would you mind typing

comp.lang.perl.misc posting guidelines

in that Google search box?

Sinan
 
J

Joe Smith

erik said:
I just read that, thanks. That sheds some light. So basically with my
example above, and due to it's dynamic nature, the only way I can get
this working is a sub-function within a subfunction.

No, not at all.

Use 'my' or 'our' to declare variables.
Do not re-declare an already declared variable.

This does not work:

my @array; # File scoped (global) variable
sub set_it {
my @array = (1,2,3); # A new, different variable
}
sub print_it {
my @array; # Yet another different variable
print "@array";
}
set_it; print_it;

But if you get rid of the 2nd and 3rd 'my', it will work.

Better yet, use 'our' instead of 'my':

sub set_it {
our @array; # Lexical access to long-lived variable
@array = (1,2,3);
}
sub print_it {
our @array; # Access to same long-lived variable
}
set_it; print_it;



I liked this line from the coping with scope.

When to Use my and When to Use local
Always use my; never use local.

I say "Always use my or our; never use local unless forced to."

-Joe
 
B

Brian McCauley

Joe said:
Use 'my' or 'our' to declare variables.
Do not re-declare an already declared variable.

This does not work:

my @array; # File scoped (global) variable
sub set_it {
my @array = (1,2,3); # A new, different variable
}
sub print_it {
my @array; # Yet another different variable
print "@array";
}
set_it; print_it;

But if you get rid of the 2nd and 3rd 'my', it will work.

Better yet, use 'our' instead of 'my':

sub set_it {
our @array; # Lexical access to long-lived variable
@array = (1,2,3);
}
sub print_it {
our @array; # Access to same long-lived variable
}
set_it; print_it;

I am a proponent of appropriate use of package variables but this in not
appropriate.

{
my @array; # Block scoped variable
sub set_it {
@array = (1,2,3);
}
sub print_it {
print "@array";
}
}

set_it; print_it;
 
T

Tad McClellan

Joe Smith said:
Do not re-declare an already declared variable.



I say "Always use my or our; never use local unless forced to."


I prefer to talk about the type of variable to use rather than
the details of how you get the type of variable that you want.

So I like to say:

Always prefer lexical over package variables, except when you can't.

usually followed by some very brief examples of "when you can't":

Perl's built-in variables

Variables that must be visible across file boundaries

Others too esoteric to mention in an introduction
 

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

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top