$
# set up a counter
$i = 0;
for($j = 0; $j < 10; $j++) {
print "j = $j\n";
$i++;
}
print "j = $j, i=$i\n";
which, when run produces:
root: perl bug.pl
j = 0
j = 1
j = 2
j = 3
j = 4
j = 5
j = 6
j = 7
j = 8
j = 9
j = 10, i=6308506
Yup, it's that solitary "$" all alone in the middle of nowhere.
it only took me about an hour and a half to spot - probably because
I was looking in entirely the wrong place as the real-life code had
a large block of comment lines between the "$" and "$i = 0;"
All I can say is aaaaaaaaaaaaaaaaaah!
Following simple good style guidelines would have spared you a lot of
time:
use warnings;
use strict;
and then it produces:
Global symbol "$i" requires explicit package name at test.pl line 8.
Global symbol "$j" requires explicit package name at test.pl line 9.
Global symbol "$j" requires explicit package name at test.pl line 9.
Global symbol "$j" requires explicit package name at test.pl line 9.
Global symbol "$j" requires explicit package name at test.pl line
10.
Global symbol "$i" requires explicit package name at test.pl line
11.
Global symbol "$j" requires explicit package name at test.pl line
14.
Global symbol "$i" requires explicit package name at test.pl line
14.
Execution of test.pl aborted due to compilation errors.
Fixing the warnings with
my $i;
then causes the error to be discovered:
Scalar found where operator expected at test.pl line 8, near "$
# set up a counter
my $i"
(Missing operator before $i?)
Global symbol "$my" requires explicit package name at test.pl line
8.
syntax error at test.pl line 8, near "$
in any case, if I'm not wrong the program was interpreted as
$$i = 0
And BTW running the script as root is not the best idea :-)
Cheers,
Matteo
It's your own fault actually. Put use strict; on top of your program and
try to reproduce misbehavior.
$$i = 0;
This statement creates a scalar reference and assigns it to $i.
This mechanism is called autovivification.
A reference is basically an integer which you can increment:
$$i = 0;
print "$i\n";
$i++;
print "$i\n";
__END__
SCALAR(0x94a3168)
155857257
Frank
--
Dipl.-Inform. Frank Seitz
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel
Blog: http://www.fseitz.de/blog
XING-Profil: http://www.xing.com/profile/Frank_Seitz2
No. A reference can be numified, and will be if you apply a numeric
operator like ++ to it. The result is not a reference, so you can't use
this to go grubbing around in arbitrary bits of memory. (You can use
unpack "P", of course, not to mention more than a few XS modules like
B::Generate.)
Ben
Not practical - it's not my original code, just my original bug :-(
What surprised me was that perl didn't see the $<extraneous stuff+whitespace># ...
and try to calculate the size of an array named by the comment, thus:
$# set ...
$i = 0;
which would at least have spat out a syntax error
Yes, this is what I meant. The conversion makes sense because
a reference is basically a memory address (plus type),
and a memory address is an integer.
> The result is not a reference, so you can't use
> this to go grubbing around in arbitrary bits of memory.
I did not say that I can do pointer arithmetic on the result.
We talk about Perl, not C :)
"# set ..." is a comment and is ignored by Perl.
The rest is syntactically ok, because Perl allows whitespace
between the dereference operator $ and the variable $i.
You miss pete's point, which was that the "#" was part of "$#...".
"$#set" is the last index of the @set array.
"$# set" is a syntax error.
> The rest is syntactically ok, because Perl allows whitespace
> between the dereference operator $ and the variable $i.
But it does not allow whitespace between the $# and the array name.
---------------------------
#!/usr/bin/perl
use warnings;
use strict;
my @set = qw(one two);
#print $#set, "\n"; # works fine
print $# set, "\n"; # syntax error
---------------------------
--
Tad McClellan
email: perl -le "print scalar reverse qq/moc.liamg\100cm.j.dat/"
The above message is a Usenet post.
I don't recall having given anyone permission to use it on a Web site.
>pete wrote:
>>
>> $
>>
>> # set up a counter
>> $i = 0;
>
>$$i = 0;
>
>This statement creates a scalar reference and assigns it to $i.
>This mechanism is called autovivification.
But, a scalar dereference can't be declared as in:
my $$i = 0;
So, from a declaration standpoint, this
$$i = 0
is actually this
my $i = \0;
a reference to a read-only constant.
>A reference is basically an integer which you can increment:
A reference is a pseudo pointer to other data (of any type).
The reference count increments as below.
$$i = \6;
print "$i\n";
print "$$i\n";
print "$$$i\n";
print "\n";
my $B = \\\\9;
print "$B\n";
print "$$B\n";
print "$$$B\n";
print "$$$$B\n";
print "$$$$$B\n";
__END__
REF(0x22ab94)
SCALAR(0x22ac44)
6
REF(0x182a9bc)
REF(0x182a99c)
REF(0x182a98c)
SCALAR(0x182a92c)
9
-sln
I talked about the original code. In that code is whitespace
between the $ and the #, i.e. # starts a comment.
On Mar 11, 7:04 am, pete <no-...@unknown.com> wrote:
>
> Not practical - it's not my original code, just my original bug :-(
I once inherited several Perl scripts with about one thousand lines
of code in each one. Unfortunately, none of those scripts had "use
strict" and "use warnings" in them.
Originally, I decided it was not practical to add "use strict" and
"use warnings" to the top of the scripts (although I did include them
in the blocks I added). Eventually, maintaining the scripts became
such a huge hassle that I "bit the bullet" and added "use strict" and
"use warnings" to the top of the scripts (and modified the code enough
to comply).
It wasn't easy adding "use strict" and "use warnings" to the files,
but now maintaining the scripts is about ten times easier.
So I recommend adding "use strict" and "use warnings" to the script
you're maintaining, even if it takes you several hours to do so. But
if you can't do that, I still recommend adding them in new blocks you
write (like inside loops, subroutines, and if-blocks). That's not
normally done, but I find it's much better than just never using
"strict" and "warnings" altogether.
-- Jean-Luc
FS> Yes, this is what I meant. The conversion makes sense because
FS> a reference is basically a memory address (plus type),
FS> and a memory address is an integer.
to be clear, a ref will return a number (the address) in a numeric
context or the TYPE(0xfdddd) style in a string context. it is not a
number but a multivalued thing that returns different things in
different contexts. it is a ref, number AND string all in the same
scalar if you want to see it that way. but once you modify it with ++ it
becomes only a number thereafter.
uri
--
Uri Guttman ------ u...@stemsystems.com -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
We can ask perl what it sees:
perl -MO=Deparse -e '
$
# a comment
$i = 0;
print $i;
'
which gives
$$i = 0;
print $i;
-e syntax OK
--
Glenn Jackman
Write a wise saying and your name will live forever. -- Anonymous
Very good explanation, except that I would not say that
a reference "returns" something, because it is a (passive) data structure.
There's no such thing in Perl. Evaluating a ref in numeric context
numifies it. It's the same as
float a;
int b = 3;
a = b;
in C, which is actually an implicit function call (though Perl caches
the result, which C doesn't).
Ben
FS> Uri Guttman wrote:
>>>>>>> "FS" == Frank Seitz <devnu...@web.de> writes:
>>
FS> Yes, this is what I meant. The conversion makes sense because
FS> a reference is basically a memory address (plus type),
FS> and a memory address is an integer.
>>
>> to be clear, a ref will return a number (the address) in a numeric
>> context or the TYPE(0xfdddd) style in a string context. it is not a
>> number but a multivalued thing that returns different things in
>> different contexts. it is a ref, number AND string all in the same
>> scalar if you want to see it that way. but once you modify it with ++ it
>> becomes only a number thereafter.
FS> Very good explanation, except that I would not say that
FS> a reference "returns" something, because it is a (passive) data structure.
hard to find a better word for what a value is in different
contexts. returns seems to work even though it isn't a sub. you can say
the same for a sub that checks wantarray or other things that 'return'
different values in different contexts (e.g. arrays). how would you say
this?
arrays return their size in scalar context and a list of their elements
in list context.
How do you know it doesn't ? The optimizer is allowed to do
anything it pleases as long as the end effect is the same.
Caching results of function calls that are known not to have
side effects is one very common optimization.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
So, spending an hour and a half chasing down a bug that Perl could have
found for you in less than a second *is* practical?
I think we define that word differently...
sherm--
Not a very good excuse. You don't even have to change the code:
perl pete
j = 0
j = 1
j = 2
j = 3
j = 4
j = 5
j = 6
j = 7
j = 8
j = 9
j = 10, i=147926066
perl -w -Mstrict pete
Global symbol "$i" requires explicit package name at pete line 4.
Global symbol "$j" requires explicit package name at pete line 6.
Global symbol "$j" requires explicit package name at pete line 6.
Global symbol "$j" requires explicit package name at pete line 6.
Global symbol "$j" requires explicit package name at pete line 7.
Global symbol "$i" requires explicit package name at pete line 8.
Global symbol "$j" requires explicit package name at pete line 10.
Global symbol "$i" requires explicit package name at pete line 10.
Execution of pete aborted due to compilation errors.
Strict wouldn't have helped in this case if the variable was declared
already but happened to contain undef:
use strict;
my $x;
...
$
#comment
$x = 1;
The only solution to this sort of problem is to keep your code in source
control, and make small commits. That way, when you break something you
can just 'git diff' or whatever and clearly see what you've changed
since last time it worked.
> Not practical - it's not my original code, just my original bug :-(
It's really not that difficult to do a minimal job. Add 'use strict;',
then keep adding 'my $foo' or 'our $foo' at the top of the file until it
stops complaining about undeclared variables. Anything nasty like
symrefs can be wrapped in an appropriate 'no strict' block.
The result won't be *good* code, but it'll be better than what you had
before and it'll stop you introducing new bugs without meaning to. You
can get round to fixing up the scoping of each piece of code as you come
to it.
Ben
I agree with the surprise...
>> >>> which would at least have spat out a syntax error
>> >>
>> >> "# set ..." is a comment and is ignored by Perl.
>> >
>> > You miss pete's point, which was that the "#" was part of "$#...".
>> >
>> > "$#set" is the last index of the @set array.
>> >
>> > "$# set" is a syntax error.
>>
>> I talked about the original code. In that code is whitespace
^^^^^^^^^^
No. I do not see whitespace there. What I see is "whitespace
interspersed with comments"; this is a different syntaxical element.
>> between the $ and the #, i.e. # starts a comment.
^^^^^^^^^^^^^^^^^^
How does one know? One cannot...
> We can ask perl what it sees:
>
> perl -MO=Deparse -e '
> $
> # a comment
> $i = 0;
> print $i;
> '
>
> which gives
>
> $$i = 0;
> print $i;
> -e syntax OK
You can ask a particular COMPILE of a particular PORT of a particular
VERSION of perl. Given that the observed behaviour is obviously a
bug, you cannot expect that any other guy would reproduce anything
similar... Experimentation has very little value as a path to a
definitive answer.
The only recourse is looking for documentation, but as we all know,
Perl is practically undocumented...
===
Let me sum up: IMO, the bug is in Perl, and not in the script. The
particular construct should not allow interspersed comments.
Hope this helps,
Ilya
The particular construct really shouldn't allow all sorts of things it
does.
--
"Six by nine. Forty two."
"That's it. That's all there is."
"I always thought something was fundamentally wrong with the universe"
> It wasn't easy adding "use strict" and "use warnings" to the files,
> but now maintaining the scripts is about ten times easier.
>
Why wasn't it easy? Surely you didn't go through and do it manually?
Uhhh, is this that "irony" thing my friends are always going on about?
Cheers.
#!/usr/bin/perl;
use strict;
use warnings;
sub appendtotop{
my ($olddir, $newdir, $filename)=@_;
my $oldfile=$olddir.$filename;
my $newfile=$newdir.$filename;
open(OLDPERL, $oldfile) || die "cannot open file $oldfile";
open(NEWPERL, ">$newfile")|| die "cannot open file $newfile";
my @contents=<OLDPERL>;
my ($isstrict, $iswarned)=(0,0);
#this looks kind of odd because I'm only checking the first ten
#lines to make sure we don't have duplicate use whatevers
#but it's more because you mentioned that you were dropping
#use strict and use warnings in blocks later on in the code
for (my $i=0; $i<10; $i++){
if($contents[$i]){
$isstrict=1 if($contents[$i]=~/use strict;/);
$iswarned=1 if($contents[$i]=~/use warnings;/);
}
}
my @appendmaterial;
$appendmaterial[0]="use strict;\n" unless($isstrict==1);
$appendmaterial[1]="use warnings;\n" unless($iswarned==1);
print NEWPERL @appendmaterial;
print NEWPERL @contents;
return;
}
my $currentperlscripts='/var/www/cgi-bin/';
my $newdirectory='/var/www/new-cgi-bin/';
opendir(OLD,$currentperlscripts) || die "Cannot opendir
$currentperlscripts: $!";
my @files=readdir(OLD);
foreach (@files){
if($_=~/\.pl/){
appendtotop($currentperlscripts, $newdirectory, $_);
}
}
There is no implicit function call here. Did you mean an implicit cast?
> (though Perl caches the result, which C doesn't).
Well, the result is stored in a. So it is "cached" in some sense.
And the compiler is of course free to cache the value of any expression
somewhere in a temporary variable if it thinks it can reuse it. So in some
code like:
x = a + b * c + d;
y = a + b * c;
a C compiler probably will "cache" the result of "a + b * c", while the
perl compiler won't.
hp
I phrased that badly: there is no C function call. But there is an
operation more complicated than simple assignment, such as might result
from the inlining of a function that does the conversion.
I was avoiding the term 'cast' because it means two different things in
C. Sometimes it means an actual conversion from one representation to
another, sometimes it means simply reinterpreting the existing bits as a
different type (casts between pointers to compatible structures, for
instance).
> > (though Perl caches the result, which C doesn't).
>
> Well, the result is stored in a. So it is "cached" in some sense.
> And the compiler is of course free to cache the value of any expression
> somewhere in a temporary variable if it thinks it can reuse it. So in some
> code like:
>
> x = a + b * c + d;
> y = a + b * c;
>
> a C compiler probably will "cache" the result of "a + b * c", while the
> perl compiler won't.
That's true, of course. OTOH, if you have
void bar(int x)
{
float f = x;
}
void foo()
{
int a = 1;
float b = a;
bar(a);
}
it won't, unless it can inline bar to assist the optimization, whereas
perl would.
Ben
I seem to be missing the part of the script which fixes all the errors
and warnings ...
hp
Hm. Perl has no data structures?
> Evaluating a ref in numeric context numifies it.
Yes. But it is perl that interprets and converts the
the scalar (= data structure), not the scalar itself.
> It's the same as
>
> float a;
> int b = 3;
> a = b;
>
> in C, which is actually an implicit function call (though Perl caches
> the result, which C doesn't).
Would you say about line 3: b "returns" a float?
I would say, the reference is "evaluated" or "interpreted"
depending on the context.
> arrays return their size in scalar context and a list of their elements
> in list context.
Paraphrase: Arrays evaluate to their size in scalar context and to a list
of their elements in list context. (?)
Blast it with piss.
Herm=
So add in a section that splits the script into subs and main then
parses each for the first instance of declared variables, and append my
to the front of each. That's likely to be a huge percentage of the
errors you'll hit adding in use strict to scripts authored by folks that
don't use, well, use strict.
You know what though - I did think he had said he had "thousands of
scripts" - but what he said was "several scripts of a thousand lines of
code". So, that changes things.
Tell you what, though - you go and create something that "fixes all the
errors and warnings" for any perl script (or any language for that
matter). You'll be rich, @#$%^.
Cheers.
The operation has about the same order of complexity as other C
operators. You wouldn't say that "+" is actually an implicit function call,
would you? Even though for some data types on some architectures an
addition is actually implemented as a function (maybe not even inlined).
> I was avoiding the term 'cast' because it means two different things in
> C. Sometimes it means an actual conversion from one representation to
> another, sometimes it means simply reinterpreting the existing bits as a
> different type (casts between pointers to compatible structures, for
> instance).
Conceptionally it is always a conversion. However, some conversions
happen to be noops. For example, the conversion int -> unsigned int is
arithmetically defined in the standard: For a nonnegative int i,
(unsigned)i is i, for a negative int i, (unsigned)i is i + UINT_MAX + 1.
If you happen to use two's complement representation (all modern
architectures, AFAIK) of signed ints, that's a noop, if you use
sign-magnitude or one's complement, you need to fiddle some bits.
Similarly for pointers: Theoretically pointers to different types could
be different at the bit level, but I don't know any modern architecture
where they are - so casts between pointers are always a noop in
practice (I also remember the quote "all struct pointers smell the
same", but I'm not sure whether that means they have to be the same at
the bit level - it's been too long since I participated in comp.std.c).
>> > (though Perl caches the result, which C doesn't).
>>
>> Well, the result is stored in a. So it is "cached" in some sense.
>> And the compiler is of course free to cache the value of any expression
>> somewhere in a temporary variable if it thinks it can reuse it. So in some
>> code like:
>>
>> x = a + b * c + d;
>> y = a + b * c;
>>
>> a C compiler probably will "cache" the result of "a + b * c", while the
>> perl compiler won't.
>
> That's true, of course. OTOH, if you have
>
> void bar(int x)
> {
> float f = x;
> }
>
> void foo()
> {
> int a = 1;
> float b = a;
> bar(a);
> }
>
> it won't, unless it can inline bar to assist the optimization, whereas
> perl would.
True. OTOH, if the C compiler can inline function calls, it will
probably also optimize away foo() completely ;-).
hp
Well, that was sort of my point. Just mechanically adding a line "use
strict;" is easy, but that almost certainly isn't what keeps the OP from
doing it. Wading through the plethora of warnings and errors and
deciding how to fix them is the real work and that cannot be easily
automated. Automatically generating my() or our() declarations isn't
quite trivial due to the irregular syntax of Perl but should be possible
- but I think the danger of covering up or creating bugs is greater than
the benefit.
I'd do it like this:
When you need to change anything in a script the very first time, you
have to spend some time anyway to read and understand it. So this is the
perfect time to add "use warnings; use strict" an fix all the resulting
errors and warnings as you go. (This is also an advantage of "use
warnings" over "-w" - it affects only the source file, not any used
modules, so you can add it one file at a time)
hp
Especially given that it was just a couple scripts, what I suggested
wouldn't be helpful at all.
Wading through the plethora of warnings and errors and
> deciding how to fix them is the real work and that cannot be easily
> automated. Automatically generating my() or our() declarations isn't
> quite trivial due to the irregular syntax of Perl but should be possible
> - but I think the danger of covering up or creating bugs is greater than
> the benefit.
>
> I'd do it like this:
>
> When you need to change anything in a script the very first time, you
> have to spend some time anyway to read and understand it. So this is the
> perfect time to add "use warnings; use strict" an fix all the resulting
> errors and warnings as you go. (This is also an advantage of "use
> warnings" over "-w" - it affects only the source file, not any used
> modules, so you can add it one file at a time)
>
> hp
>
Check.
Cheers.