List Variable becomes undefined inexplicably

M

mattbreedlove

Using Perl v5.8.4
This type of problem has plagued me over the years several times. Each
time I have had to rearrange the way that my PERL script was designed
to work around it.

At this point I think its a bug or something really strange.
I was able to reproduce it with the test script below.

The issue is that a global list variable that is built inside
subroutine A, iterated over in a foreach loop in subroutine B, is then
magically undefined after the foreach loop if the loop contains a
command pipe with a while loop.

According the laws of PERL that I am used to when you run the below
script you *should* get:
main_before-ITERATE_HOSTS -apple orange durian-
start_inside_ITERATE_HOSTS -apple orange durian-
end_inside_ITERATE_HOSTS -apple orange durian-
main_after-ITERATE_HOSTS -apple orange durian-

However instead you get
main_before-ITERATE_HOSTS -apple orange durian-
start_inside_ITERATE_HOSTS -apple orange durian-
end_inside_ITERATE_HOSTS - -
main_after-ITERATE_HOSTS - -

Any ideas?

Here is the test script:


#!perl

SETUP_HOSTS();
print "main_before-ITERATE_HOSTS -@SLAVE_HOSTS-\n";
ITERATE_HOSTS();
print "main_after-ITERATE_HOSTS -@SLAVE_HOSTS-\n";

sub SETUP_HOSTS {

push(@SLAVE_HOSTS, apple);
push(@SLAVE_HOSTS, orange);
push(@SLAVE_HOSTS, durian);

}

sub ITERATE_HOSTS {
print "start_inside_ITERATE_HOSTS -@SLAVE_HOSTS-\n";
foreach (@SLAVE_HOSTS) {
$REMOTE_HOST="$_";

#open(CMD, "$SSH $REMOTE_HOST 'do some stuff'|");
while(<CMD>) {
chomp;
$LINE="$_";
if ( "$LINE" =~ /service id\=/ ) {
print "blah\n";
}
}
close(CMD);
}
print "end_inside_ITERATE_HOSTS -@SLAVE_HOSTS-\n";

}
 
K

kens

Using Perl v5.8.4
This type of problem has plagued me over the years several times. Each
time I have had to rearrange the way that my PERL script was designed
to work around it.

At this point I think its a bug or something really strange.
I was able to reproduce it with the test script below.

The issue is that a global list variable that is built inside
subroutine A, iterated over in a foreach loop in subroutine B, is then
magically undefined after the foreach loop if the loop contains a
command pipe with a while loop.

According the laws of PERL that I am used to when you run the below
script you *should* get:
main_before-ITERATE_HOSTS -apple orange durian-
start_inside_ITERATE_HOSTS -apple orange durian-
end_inside_ITERATE_HOSTS -apple orange durian-
main_after-ITERATE_HOSTS -apple orange durian-

However instead you get
main_before-ITERATE_HOSTS -apple orange durian-
start_inside_ITERATE_HOSTS -apple orange durian-
end_inside_ITERATE_HOSTS - -
main_after-ITERATE_HOSTS - -

Any ideas?

Here is the test script:

#!perl

use strict;
use warnings;

my @SLAVE_HOSTS;
SETUP_HOSTS();
print "main_before-ITERATE_HOSTS -@SLAVE_HOSTS-\n";
ITERATE_HOSTS();
print "main_after-ITERATE_HOSTS -@SLAVE_HOSTS-\n";

sub SETUP_HOSTS {

Note that you will be forced to quote barewords when using strict:
push(@SLAVE_HOSTS, apple);
push(@SLAVE_HOSTS, orange);
push(@SLAVE_HOSTS, durian);

}

sub ITERATE_HOSTS {
print "start_inside_ITERATE_HOSTS -@SLAVE_HOSTS-\n";

Here is part of your problem - using the default variable ($_)
It would be better to define a new variable
foreach my $slave (@SLAVE_HOSTS)
foreach (@SLAVE_HOSTS) {
Useless use of quotes
my $REMOTE_HOST = $_;
$REMOTE_HOST="$_";

#open(CMD, "$SSH $REMOTE_HOST 'do some stuff'|");

Note that you are using $_ as the loop variable here.
This is not good, as $_ is one of the elements of @SLAVE_HOSTS (see
above foreach)
When you modify $_ you modify an element of @SLAVE_HOSTS - essentially
you are 'undef' ing the whole array since the 'OPEN' statement is
commented.
while(<CMD>) {
chomp;
Useless use of quotes
my $LINE = $_;
$LINE="$_";
Useless use of quotes also no need to backslash the equal sign
if ( "$LINE" =~ /service id\=/ ) {
print "blah\n";
}
}
close(CMD);
}
print "end_inside_ITERATE_HOSTS -@SLAVE_HOSTS-\n";

}

HTH, Ken
 
M

mattbreedlove

The only reason the open command was commented was so that anyone
could run the test script to reproduce the problem. I tested the test
script with or without the commented "open" command so its not like
the array is getting undefined by the absence of input to the while
loop. It is certainly not the root cause.

I believe there is a place for strict and warnings. That place is not
necessarily in a quick test script to illustrate a completely
unrelated problem. Thanks

So the question remains, why is this happening? I appreciate the
suggestions to define a new variable in the instantiation of the
foreach/while loops, but the fact is that "should" not be required.

foreach (@SLAVE_HOSTS) {
chomp;
$REMOTE_HOST="$_";

open(CMD, "ping $REMOTE_HOST|");
while(<CMD>) {
chomp;
$LINE="$_";
}
}
print "@SLAVE_HOSTS\n";

PERL should be able to keep track of the frame of reference and values
for two discreet "$_" in nested iterative loops without blowing away a
parent array

This should be the same:
while(<CMD>) {
chomp;
$LINE="$_";
}

as this:

while ( $LINE = <CMD> ) {
chomp $LINE;

Such a "waste" of a precious line when it could be done with one line
less, but again the fact remains that this should work. Correct?
 
B

Brian McCauley

PERL should be able to keep track of the frame of reference and values
for two discreet "$_" in nested iterative loops without blowing away a
parent array

What you are actually saying is that the implicit assignment to $_ in
while(<FILEHANDLE>) should be implicit _localisation_ and assignment.
No argument from me there. If I had a time machine I'd go back and fix
it but there are probably moderately good reasons why it would be bad
to fix it now.

I always do a local(*_) before a while loop using implicit assignment
of $_ and I suggest that everyone should do so (or not use the
implicit assignment at all).

Do not be temped to do local($_) as nasty things will happen if the
"parent array" happens to be tied.

BTW: Please don't call Perl "PERL" as you'll just needlessly piss
people off.
 
J

John W. Krahn

PERL should be able to keep track of the frame of reference and values
for two discreet "$_" in nested iterative loops without blowing away a
parent array

This "PERL" you speak of may be able to do that but here we use Perl which has
only one $_ variable.



John
 
K

kens

The only reason the open command was commented was so that anyone
could run the test script to reproduce the problem. I tested the test
script with or without the commented "open" command so its not like
the array is getting undefined by the absence of input to the while
loop. It is certainly not the root cause.

I believe there is a place for strict and warnings. That place is not
necessarily in a quick test script to illustrate a completely
unrelated problem. Thanks

I 'always' use strict and warnings. How do you know you don't have a
problem that is related until you try it?
So the question remains, why is this happening?

Because of the way the foreach loop works - issue the command "perldoc
perlsyn" for more information.
I appreciate the
suggestions to define a new variable in the instantiation of the
foreach/while loops, but the fact is that "should" not be required.

Playing the devil's advocate here. How would you refer to the $_
variable from the outer loop in the inner loop? Are you going to
force a label on the loops, so you can say OUTER_LOOP:$_ ?
foreach (@SLAVE_HOSTS) {
chomp;
$REMOTE_HOST="$_";

open(CMD, "ping $REMOTE_HOST|");
while(<CMD>) {
chomp;
$LINE="$_";
}}

print "@SLAVE_HOSTS\n";

PERL should be able to keep track of the frame of reference and values
for two discreet "$_" in nested iterative loops without blowing away a
parent array

This should be the same:
while(<CMD>) {
chomp;
$LINE="$_";

}

as this:

while ( $LINE = <CMD> ) {
chomp $LINE;

Such a "waste" of a precious line when it could be done with one line
less, but again the fact remains that this should work. Correct?

Not sure what you are referring to by 'this'. If you mean that $LINE
should contain the same value as $_ within the loop, I would say
yes.

Ken
 
C

Charlton Wilbur

mb> I believe there is a place for strict and warnings. That
mb> place is not necessarily in a quick test script to illustrate
mb> a completely unrelated problem. Thanks

However, it *is* in a script you are asking human beings to help you
with; if you haven't made adequate use of the automatic tools
available to you, why are you wasting other people's time?

Charlton
 
M

mattbreedlove

Honestly, I am intrigued by this passion about not referring to PERL
in caps. Correct me if I am wrong, but PERL is an Acronym for
Practical Extraction and Reporting Language.

Generally, acronyms are written in caps. If anything I could see
saying that it should be written P.E.R.L

So what is all this frustration about?
 
U

Uri Guttman

m> Honestly, I am intrigued by this passion about not referring to
m> PERL in caps. Correct me if I am wrong, but PERL is an Acronym for
m> Practical Extraction and Reporting Language.

you are wrong. read the perlfaq for the real history of the name.

m> Generally, acronyms are written in caps. If anything I could see
m> saying that it should be written P.E.R.L

wronger.

it is Perl for the language and perl for the program. never PERL which
marks you as someone who doesn't know Perl.

m> So what is all this frustration about?

being technically correct and pedantic is a useful attribute in a
programmer. using the correct name is a point that is worth defending.

uri
 
B

benkasminbullock

No, the open is not the root cause. The root cause is the input
operator <CMD> that you did _not_ comment out. In general, it is best
to post only relevant code, replacing such things as reading external
files with the equivalent assignment statements, for example, or using
the special <DATA> file handle so people can cut-and-paste your
program.




Fewer people will analyze your code if you don't have use strict.
From the number of responses to the original poster's query, it
actually looks like the opposite: many more people will analyze one's
code if one doesn't have "use strict;".

Just an observation.
 
M

mattbreedlove

it is Perl for the language and perl for the program. never PERL which
marks you as someone who doesn't know Perl.

Is that kind of like saying someone must not know how to drive well
unless they know how to fix cars?
 
T

Tad McClellan

I believe there is a place for strict and warnings.


That's nice but it is not relevant to getting someone to examine
your problem.

What is relevant to getting the interest of others is what the
_others_ think.

I've been here for quite some time, I know how a program without
warnings and strict are received here (the most common reaction
is to simply move on to the next question).

I was trying to help you avoid being ignored. Disregard my
comments if being ignored does not concern you.

That place is not
necessarily in a quick test script to illustrate a completely
unrelated problem.


warnings and strictures very often find bugs in programs. Many
people will find it demeaning to be asked to do the work of a machine.

It is inconsiderate of your audience to post without warnings and strict.



Off to the killfile you go then.

PERL should be able to keep track of the frame of reference


There is no PERL.

There is a Perl and there is a perl.

This should be the same:
while(<CMD>) {
chomp;
$LINE="$_";
}

as this:

while ( $LINE = <CMD> ) {
chomp $LINE;

Such a "waste" of a precious line when it could be done with one line
less,


The number of lines is not the point.

The point is maintainability.

Being direct is easier to read and understand than being circuitous.
 
R

RedGrittyBrick

Is that kind of like saying someone must not know how to drive well
unless they know how to fix cars?

No, it is more like drivers being required to understand the difference
between a Ford and a ford.
 
C

Charlton Wilbur

mb> Is that kind of like saying someone must not know how to drive
mb> well unless they know how to fix cars?

No, it's more like saying that a mechanic who refers to bits of the
engine by the wrong names is unlikely to be competent.

PERL versus Perl or perl is a shibboleth: using the wrong one
indicates that you're likely to be a clueless novice. If you really
want people to think you are a clueless novice, and you really want
people to treat you as a clueless novice, by all means, carry on.

And continuing to use PERL after this has been pointed out generally
marks you as a clueless novice who's stubborn enough to persist in his
cluelessness, which means that wise people will, for the most part,
just silently plonk you.

Honestly, you came here for help, and the people you came to for help,
most of whom have more experience than you, have made a couple
recommendations. Now, the PERL/Perl/perl shibboleth is not really an
important one technically, but the way you react to it is very
telling. More important technically, and more telling, is the way you
reacted to the advice to use strictures and warnings. If you aren't
interested in listening to or following advice, why ask for it?

Charlton
 

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

Forum statistics

Threads
474,001
Messages
2,570,255
Members
46,853
Latest member
GeorgiaSta

Latest Threads

Top