Where is the error "Use of uninitialized value in string ne" coming from? - perl

Where is the uninitialised value in the below code?
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (($url = $sites[$sitecount]) ne undef) {
$sitecount++;
}
Output:
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.

You can't use undef in a string comparison without a warning.
if ("a" ne undef) { ... }
will raise a warning. If you want to test if a variable is defined or not, use:
if (defined $var) { ... }
Comments about the original question:
That's a strange way to iterate over an array. The more usual way of doing this would be:
foreach my $url (#sites) { ... }
and drop the $sitecount variable completely, and don't overwrite $url in the loop body. Also drop the undef value in that array. If you don't want to remove that undef for some reason (or expect undefined values to be inserted in there), you could do:
foreach my $url (#sites) {
next unless defined $url;
...
}
If you do want to test for undefined with your form of loop construct, you'd need:
while (defined $sites[$sitecount]) {
my $url = $sites[$sitecount];
...
$sitecount++;
}
to avoid the warnings, but beware of autovivification, and that loop would stop short if you have undefs mixed in between other live values.

The correct answers have already been given (defined is how you check a value for definedness), but I wanted to add something.
In perlop you will read this description of ne:
Binary "ne" returns true if the left argument is stringwise not equal
to the right argument.
Note the use of "stringwise". It basically means that just like with other operators, such as ==, where the argument type is pre-defined, any arguments to ne will effectively be converted to strings before the operation is performed. This is to accommodate operations such as:
if ($foo == "1002") # string "1002" is converted to a number
if ($foo eq 1002) # number 1002 is converted to a string
Perl has no fixed data types, and relies on conversion of data. In this case, undef (which coincidentally is not a value, it is a function: undef(), which returns the undefined value), is converted to a string. This conversion will cause false positives, that may be hard to detect if warnings is not in effect.
Consider:
perl -e 'print "" eq undef() ? "yes" : "no"'
This will print "yes", even though clearly the empty string "" is not equal to not defined. By using warnings, we can catch this error.
What you want is probably something like:
for my $url (#sites) {
last unless defined $url;
...
}
Or, if you want to skip to a certain array element:
my $start = 1;
for my $index ($start .. $#sites) {
last unless defined $sites[$index];
...
}
Same basic principle, but using an array slice, and avoiding indexes:
my $start = 1;
for my $url (#sites[$start .. $#sites]) {
last unless defined $url;
...
}
Note that the use of last instead of next is the logical equivalent of your while loop condition: When an undefined value is encountered, the loop is exited.
More debugging: http://codepad.org/Nb5IwX0Q
If you, like in this paste above, print out the iteration counter and the value, you will quite clearly see when the different warnings appear. You get one warning for the first comparison "a" ne undef, one for the second, and two for the last. The last warnings come when $sitecount exceeds the max index of #sites, and you are comparing two undefined values with ne.

Perhaps the message would be better to understand if it was:
You are trying to compare an uninitialized value with a string.
The uninitialized value is, of course, undef.
To explicitely check if $something is defined, you need to write
defined $something

ne is for string comparison, and undef is not a string:
#!/usr/bin/perl
use warnings;
('l' ne undef) ? 0 : 0;
Use of uninitialized value in string ne at t.pl line 3.
It does work, but you get a [slightly confusing] warning (at least with use warnings) because undef is not an "initialized value" for ne to use.
Instead, use the operator defined to find whether a value is defined:
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (defined $sites[$sitecount]) { # <----------
$url = $sites[$sitecount];
# ...
$sitecount++;
}
... or loop over the #sites array more conventionally, as Mat explores in his answer.

Related

Perl: undef element in array

I defined some elements in an array:
my #arrTest;
$arrTest[0]=1;
$arrTest[2]=2;
#Result for #arrTest is: (1, undef, 2)
if($arrTest[1]==0)
{
print "one\n";
}
elsif($arrTest[1] == undef)
{
print "undef\n";
}
I think it should print "undef". But it prints "one" here...
Does it mean $arrTest[1]=undef=0?
How can I modify the "if condition" to distinguish the "undef" in array element?
The operator == in the code $arrTest[1] == 0 puts $arrTest[1] in numeric context and so its value gets converted to a number if needed, as best as the interpreter can do, and that is used in the comparison. And when a variable in a numeric test hasn't been defined a 0 is used so the test evaluates to true (the variable stays undef).
Most of the time when this need be done we get to hear about it (there are some exceptions) -- if we have use warnings; that is (best at the beginning of the program)† Please always have warnings on, and use strict. They directly help.
To test for defined-ness there is defined
if (not defined $arrTest[1]) { ... }
† A demo
perl -wE'say "zero" if $v == 0; say $v; ++$v; say $v'
The -w enables warnings. This command-line program prints
Use of uninitialized value $v in numeric eq (==) at -e line 1.
zero
Use of uninitialized value $v in say at -e line 1.
1
Note how ++ doesn't warn, one of the mentioned exceptions.

What is "Use of unitialized value $. in range (or flip)" trying to tell me in Perl

I have the following code snippet in Perl:
my $argsize = #args;
if ($argsize >1){
foreach my $a ($args[1..$argsize-1]) {
$a =~ s/(.*[-+*].*)/\($1\)/; # if there's a math operator, put in parens
}
}
On execution I'm getting "Use of unitialized value $. in range (or flip) , followed by Argument "" isn't numeric in array element at... both pointing to the foreach line.
Can someone help me decipher the error message (and fix the problem(s))? I have an array #args of strings. The code should loop through the second to n't elements (if any exist), and surround individual args with () if they contain a +,-, or *.
I don't think the error stems from the values in args, I think I'm screwing up the range somehow... but I'm failing when args has > 1 element. an example might be:
<"bla bla bla"> <x-1> <foo>
The long and short of it is - your foreach line is broken:
foreach my $a (#args[1..$argsize-1]) {
Works fine. It's because you're using a $ which says 'scalar value' rather than an # which says array (or list).
If you use diagnostics you get;
Use of uninitialized value $. in range (or flip) at
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you
the name of the variable (if any) that was undefined. In some cases
it cannot do this, so it also tells you what operation you used the
undefined value in. Note, however, that perl optimizes your program
and the operation displayed in the warning may not necessarily appear
literally in your program. For example, "that $foo" is usually
optimized into "that " . $foo, and the warning will refer to the
concatenation (.) operator, even though there is no . in
your program.
You can reproduce this error by:
my $x = 1..3;
Which is actually pretty much what you're doing here - you're trying to assign an array value into a scalar.
There's a load more detail in this question:
What is the Perl context with range operator?
But basically: It's treating it as a range operator, as if you were working your way through a file. You would be able to 'act on' particular lines in the file via this operator.
e.g.:
use Data::Dumper;
while (<DATA>) {
my $x = 2 .. 3;
print Dumper $x;
print if $x;
}
__DATA__
line one
another line
third line
fourth line
That range operator is testing line numbers - and because you have no line numbers (because you're not iterating a file) it errors. (But otherwise - it might work, but you'd get some really strange results ;))
But I'd suggest you're doing this quite a convoluted way, and making (potentially?) an error, in that you're starting your array at 1, not zero.
You could instead:
s/(.*[-+*].*)/\($1\)/ for #args;
Which'll have the same result.
(If you need to skip the first argument:
my ( $first_arg, #rest ) = #args;
s/(.*[-+*].*)/\($1\)/ for #rest;
But that error at runtime is the result of some of the data you're feeding in. What you've got here though:
use strict;
use warnings;
my #args = ( '<"bla bla bla">', '<x-1>', '<foo>' );
print "Before #args\n";
s/(.*[-+*].*)/\($1\)/ for #args;
print "After: #args\n";

Where does a Perl subroutine get values missing from the actual parameters?

I came across the following Perl subroutine get_billable_pages while chasing a bug. It takes 12 arguments.
sub get_billable_pages {
my ($dbc,
$bill_pages, $page_count, $cover_page_count,
$domain_det_page, $bill_cover_page, $virtual_page_billing,
$job, $bsj, $xqn,
$direction, $attempt,
) = #_;
my $billable_pages = 0;
if ($virtual_page_billing) {
my #row;
### Below is testing on the existence of the 11th and 12th parameters ###
if ( length($direction) && length($attempt) ) {
$dbc->xdb_execute("
SELECT convert(int, value)
FROM job_attribute_detail_atmp_tbl
WHERE job = $job
AND billing_sub_job = $bsj
AND xqn = $xqn
AND direction = '$direction'
AND attempt = $attempt
AND attribute = 1
");
}
else {
$dbc->xdb_execute("
SELECT convert(int, value)
FROM job_attribute_detail_tbl
WHERE job = $job
AND billing_sub_job = $bsj
AND xqn = $xqn
AND attribute = 1
");
}
$cnt = 0;
...;
But is sometimes called with only 10 arguments
$tmp_det = get_billable_pages(
$dbc2,
$row[6], $row[8], $row[7],
$domain_det_page, $bill_cover_page, $virtual_page_billing,
$job1, $bsj1, $row[3],
);
The function does a check on the 11th and 12th arguments.
What are the 11th and 12th arguments when the function is passed only 10 arguments?
Is it a bug to call the function with only 10 arguments because the 11th and 12th arguments end up being random values?
I am thinking this may be the source of the bug because the 12th argument had a funky value when the program failed.
I did not see another definition of the function which takes only 10 arguments.
The values are copied out of the parameter array #_ to the list of scalar variables.
If the array is shorter than the list, then the excess variables are set to undef. If the array is longer than the list, then excess array elements are ignored.
Note that the original array #_ is unmodified by the assignment. No values are created or lost, so it remains the definitive source of the actual parameters passed when the subroutine is called.
ikegami suggested that I should provide some Perl code to demonstrate the assignment of arrays to lists of scalars. Here is that Perl code, based mostly on his edit
use strict;
use warnings;
use Data::Dumper;
my $x = 44; # Make sure that we
my $y = 55; # know if they change
my #params = (8); # Make a dummy parameter array with only one value
($x, $y) = #params; # Copy as if this is were a subroutine
print Dumper $x, $y; # Let's see our parameters
print Dumper \#params; # And how the parameter array looks
output
$VAR1 = 8;
$VAR2 = undef;
$VAR1 = [ 8 ];
So both $x and $y are modified, but if there are insufficient values in the array then undef is used instead. It is as if the source array was extended indefinitely with undef elements.
Now let's look at the logic of the Perl code. undef evaluates as false for the purposes of conditional tests, but you apply the length operator like this
if ( length($direction) && length($attempt) ) { ... }
If you have use warnings in place as you should, Perl would normally produce a Use of uninitialized value warning. However length is unusual in that, if you ask for the length of an undef value (and you are running version 12 or later of Perl 5) it will just return undef instead of warning you.
Regarding "I did not see another definition of the function which takes only 10 arguments", Perl doesn't have function templates like C++ and Java - it is up to the code in the subroutine to look at what it has been passed and behave accordingly.
No, it's not a bug. The remaining arguments are "undef" and you can check for this situation
sub foo {
my ($x, $y) = #_;
print " x is undef\n" unless defined $x;
print " y is undef\n" unless defined $y;
}
foo(1);
prints
y is undef

What perl code samples can lead to undefined behaviour?

These are the ones I'm aware of:
The behaviour of a "my" statement modified with a statement modifier conditional or loop construct (e.g. "my $x if ...").
Modifying a variable twice in the same statement, like $i = $i++;
sort() in scalar context
truncate(), when LENGTH is greater than the length of the file
Using 32-bit integers, "1 << 32" is undefined. Shifting by a negative number of bits is also undefined.
Non-scalar assignment to "state" variables, e.g. state #a = (1..3).
One that is easy to trip over is prematurely breaking out of a loop while iterating through a hash with each.
#!/usr/bin/perl
use strict;
use warnings;
my %name_to_num = ( one => 1, two => 2, three => 3 );
find_name(2); # works the first time
find_name(2); # but fails this time
exit;
sub find_name {
my($target) = #_;
while( my($name, $num) = each %name_to_num ) {
if($num == $target) {
print "The number $target is called '$name'\n";
return;
}
}
print "Unable to find a name for $target\n";
}
Output:
The number 2 is called 'two'
Unable to find a name for 2
This is obviously a silly example, but the point still stands - when iterating through a hash with each you should either never last or return out of the loop; or you should reset the iterator (with keys %hash) before each search.
These are just variations on the theme of modifying a structure that is being iterated over:
map, grep and sort where the code reference modifies the list of items to sort.
Another issue with sort arises where the code reference is not idempotent (in the comp sci sense)--sort_func($a, $b) must always return the same value for any given $a and $b.

Why does Perl complain about "Use of uninitialized value" in my CGI script?

I am cleaning my Perl code for production release and came across a weird warning in the Apache error log.
It says:
[Thu Nov 5 15:19:02 2009] Clouds.pm: Use of uninitialized value $name in substitution (s///) at /home/mike/workspace/olefa/mod-bin/OSA/Clouds.pm line 404.
The relevant code is here:
my $name = shift #_;
my $name_options = shift #_;
$name_options = $name_options eq 'unique' ? 'u'
: $name_options eq 'overwrite' ? 'o'
: $name_options eq 'enumerate' ? 'e'
: $name_options =~ m/^(?:u|o|e)$/ ? $name_options
: q();
if ($name_options ne 'e') {
$name =~ s/ /_/g;
}
So, why the warning of an uninitialized variable as it is clearly initialized?
The warning simply means that $name was never filled with a value, and you tried doing a substitution operation (s///) on it. The default value of a variable is undefined (undef).
Looking back through your script, $name gets its value from #_. This means either #_ was empty, or had its first value as undef.
Depending on what your subroutine needs to do, validate your values before you use them. In this case, since you need something in $name, croak if there isn't something in that variable. You'll get an error message from the perspective of the caller and you'll find the culprit.
Also, you can lose the complexity of the conditional operator chain by making it a hash lookup, which also gives you a chance to initialize $name_option. In your fallback case, you leave $name_option undefined:
use 5.010;
use Carp;
BEGIN {
my %valid_name_options = map {
$_
substr( $_, 0, 1 ),
} qw( unique overwrite enumerate );
some_sub {
my( $name, $name_options ) = #_;
croak( "Name is not defined!" ) unless defined $name;
$name_options = $valid_name_options{$name_options} // '';
if ($name_options ne 'e') {
$name =~ s/ /_/g;
}
...
}
}
Debug by divide and conquer
It is quite usual to run into bugs that are "obviously impossible" --- at the first glance. I usually try to confirm my assumptions with simple print statements (or equivalent ways to get some information back from the program: For CGI scripts, a simple print can ruin your headers).
So, I would put a statement like
print "testing: ", defined($name)? "defined: '$name'" : "undef", "\n";
into the code at the suspected line. You might be surprised about the possible output options:
"testing: undef" --- this means that your function was called with an undefined first argument. Unlikely but possible. You may want to use caller() to find out from where it was called and check the data there.
no output at all! Maybe you look at the wrong source file or at the wrong line. (I don't suspect that's the case here, but it does happen to me).
"testing: defined: 'some data'" --- oops. ask a question on stackoverflow.