ruqui has asked for the wisdom of the Perl Monks concerning the following question:
Hi monks!
I don't know if it's possible what I try to achieve, but definitely this is the place to ask :)
It happens to me frequently that I got the error:
Use of uninitialized value in ...
when I'm printing several values together using printf. Obviously, if I debug the application I can determine the uninitialized variable, but that's usually a lengthy and boring process that I'd like to avoid if possible.
Is there any way to know which variable is the culprit without debugging? In other words, is it possible somehow for perl to specify in the output error something like:
Use of uninitialized value in variable $myvar in ...
Thanks in advance!
NOTICE
Just to be clear, I know how to avoid these type of errors either by a) initializing the variables with a default value and/or b) checking whether or not they are defined. I don't want to add any kind of extra checking code to avoid the errors, I just want to know, when the error happens, which variable is not initialized correctly.
Re: determine the variable causing the error: Use of uninitialized value
by haukex (Bishop) on Apr 13, 2017 at 18:29 UTC
|
$ perl -wMstrict -e 'my $myvar; print $myvar; printf "%s", $myvar'
Use of uninitialized value $myvar in print at -e line 1.
Use of uninitialized value $myvar in printf at -e line 1.
Could you show some code that demonstrates the problem (SSCCE)? Also, which version of Perl are you using? Update: Just to be clear, by "the problem" I mean the variable name not showing up in the warning message. (Also, s/x/myvar/g)
| [reply] [d/l] [select] |
|
c:\@Work\Perl>perl -wMstrict -le
"print qq{perl version: $] \n};
;;
my $x;
print $x;
printf '%s', $x;
"
perl version: 5.008009
Use of uninitialized value in print at -e line 1.
Use of uninitialized value in printf at -e line 1.
perl version: 5.010001
Use of uninitialized value $x in print at -e line 1.
Use of uninitialized value $x in printf at -e line 1.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
AnomalousMonk it's correct what you say, but it only work with scalars, it doesn't work with more complex structures like hashes (or references to hashes):
EXAMPLE:
perl -wMstrict -le 'print qq{perl version: $] \n};;; my $x; my %y= ("
+h"=>1, "i"=>2); print $x, $y{x}; printf "%s", $x;'
OUTPUT:
perl version: 5.018002
Use of uninitialized value $x in print at -e line 1.
Use of uninitialized value in print at -e line 1.
Use of uninitialized value $x in printf at -e line 1.
</b>
| [reply] [d/l] [select] |
|
|
|
|
Re: determine the variable causing the error: Use of uninitialized value
by LanX (Sage) on Apr 13, 2017 at 18:41 UTC
|
In most cases you get told which variable causes the problem in which line.
Prepending something like $var//=""; is a quick fix if you receive the variable from an uncontrolled source.
Otherwise try initializing strings, like my $str=""
From a general perspective:
This warning is probably the most disputed, if you just want to silence it in your scope, try
no warnings "uninitialized"
HTH!
| [reply] [d/l] [select] |
|
Rolf, I'm aware of the solution you proposed, but it is unpractical for me, most of the times I'm dealing with large hash structures and big input data, it's inefficient to initialize everything just to avoid an error; in fact, I'm usually don't even want to supress the error (this usually means the input data is wrong and I need to deal with it), I just want a quicker way to determine which variable is involved.
| [reply] |
|
... I'm dealing with large hash structures and big input data ... I'm usually don't even want to supress the error (this usually means the input data is wrong and I need to deal with it) ...
The point raised by haukex here is critical: you're really dealing with input data validation, not a way to flag the use of an undef value that might be present if your input data was invalid. In other words, don't bother trying to lock the barn door after the horse has bolted. The ideal time to deal with bad data is when you get it, not after it's drifted downstream and had a chance to gum up other processes.
The only way I can think of to validate a data structure is to write a validation function specific to that structure and invoke it as soon as possible after the structure is captured:
my %bigdata = capture_big_input_data($inputstream);
validate_bigdata(\%bigdata);
Obviously, the bigger your data, the bigger your validation job, but that's life. (You may even decide that certain missing data fields can, in fact, be fixed up with a zero, an empty string or whatever; if so, the validate_bigdata() function is the ideal place to do this patching.)
The other variation on this theme is when you read your input data on a line-by-line or block-by-block basis. In this case, you must figure out a way to validate each line or block as it is read — at least, that's what I would do. Anyway, you get the idea...
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
|
|
|
sub seek_undef {
my ($ref,$path) = (@_,"");
if (ref $ref eq 'HASH')
{ seek_undef($ref->{$_}, $path."{$_}") for keys %$ref }
elsif (ref $ref eq 'ARRAY')
{ seek_undef($ref->[$_], $path."[$_]") for 0..$#$ref }
else { defined $ref or warn "undef at $path\n" }
}
seek_undef( {
foo => { quz=>{ abc=>"def", ghi=>undef } },
bar => { baz=>[ "jkl", undef, { ooo=>undef } ],
blah=>undef }, uuu => undef,
} );
__END__
undef at {foo}{quz}{ghi}
undef at {bar}{baz}[1]
undef at {bar}{baz}[2]{ooo}
undef at {bar}{blah}
undef at {uuu}
| [reply] [d/l] [select] |
|
use strict;
use warnings;
warn "version $]";
my %hash=(a=>undef,b=>[]);
my $h=\%hash;
my @array=({a=>undef});
my $a=\@array;
warn "hash $hash{a}";
warn "hoa $hash{b}[0]";
warn "hashref $h->{a}";
warn "hoa ref $h->{b}[0]";
warn "array $array[1] ";
warn "aoh $array[0]{a} ";
warn "array ref $a->[1] ";
warn "aoh ref $a->[0]{a} ";
version 5.014002 at /home/lanx/pm/warn_undef.pl line 4.
Use of uninitialized value $hash{"a"} in concatenation (.) or string a
+t /home/lanx/pm/warn_undef.pl line 13.
hash at /home/lanx/pm/warn_undef.pl line 13.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 14.
hoa at /home/lanx/pm/warn_undef.pl line 14.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 16.
hashref at /home/lanx/pm/warn_undef.pl line 16.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 17.
hoa ref at /home/lanx/pm/warn_undef.pl line 17.
Use of uninitialized value $array[1] in concatenation (.) or string at
+ /home/lanx/pm/warn_undef.pl line 19.
array at /home/lanx/pm/warn_undef.pl line 19.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 20.
aoh at /home/lanx/pm/warn_undef.pl line 20.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 22.
array ref at /home/lanx/pm/warn_undef.pl line 22.
Use of uninitialized value in concatenation (.) or string at /home/lan
+x/pm/warn_undef.pl line 23.
aoh ref at /home/lanx/pm/warn_undef.pl line 23.
| [reply] [d/l] [select] |
|
|
"...it's inefficient to initialize everything..."
"Everything has it's price" (Thomas Mann)
«The Crux of the Biscuit is the Apostrophe»
Furthermore I consider that Donald Trump must be impeached as soon as possible
| [reply] |
Re: determine the variable causing the error: Use of uninitialized value
by haukex (Bishop) on Apr 14, 2017 at 10:38 UTC
|
From your various posts in this thread:
I'm dealing with large hash structures and big input data ... [validation] is highly inefficient
Please clarify. How big and deep are your data structures? Did you try running the code I posted here on your data, and how long did it take?
Also, how are you accessing your data structure? Directly, as in printf "%s", $data->{foo}{bar}{quz}{baz}, do you copy subsections into temporary variables, do you walk the structure recursively, etc.? Again, please see SSCCE, if you could show us something that's actually representative of your code, we can give better advice.
I don't want to add any kind of extra checking code to avoid the error ... I just want to know if it's possible (without writing any specific checking code) to know the name of the variable that produces the error
I believe that Rolf showed (Update: now even including a post by the P5Porter who implemented it himself!) conclusively that in many cases, this isn't built into Perl, so the answer to your question is no. You'll have to add the checks yourself, even if you "don't want to" :-P
You could check the arguments:
use Carp qw/cluck/;
sub myprintf {
cluck "myprintf: undef argument(s)"
if grep {!defined} @_;
printf @_
}
myprintf "%s-%s-%s-%s\n", 'quz', undef, 'baz', undef;
__END__
myprintf: undef argument(s) at - line 4.
main::myprintf("%s-%s-%s-%s\x{a}", "quz", undef, "baz", undef) cal
+led at - line 7
Use of uninitialized value in printf at - line 5.
Use of uninitialized value in printf at - line 5.
quz--baz-
Or you could check for undefs when accessing the data:
use Carp;
sub dive {
my ($ref,@path) = @_;
for my $i (0..$#path) {
$ref = $path[$i]=~/^\[(\d+)\]$/
? $ref->[$1] : $ref->{$path[$i]};
if (!defined $ref) {
carp "undef at: ".join('',
map {/^\[\d+\]$/?$_:"{$_}"} @path[0..$i]);
return undef;
}
}
return $ref;
}
my $data = { this => { is => { a =>
{ deep => { structure => [ 'abc', 'def', undef ]
} } } } };
printf "<%s>\n", dive($data, qw/ this is a deep structure [0] /);
printf "<%s>\n", dive($data, qw/ this is a deep structure [3] /);
printf "<%s>\n", dive($data, qw/ this is an example /);
__END__
<abc>
undef at: {this}{is}{a}{deep}{structure}[3] at - line 10.
main::dive(HASH(0x9e47a4), "this", "is", "a", "deep", "structure",
+ "[3]") called at - line 22
Use of uninitialized value in printf at - line 22.
<>
undef at: {this}{is}{an} at - line 10.
main::dive(HASH(0x9e47a4), "this", "is", "an", "example") called a
+t - line 23
Use of uninitialized value in printf at - line 23.
<>
| [reply] [d/l] [select] |
Re: determine the variable causing the error: Use of uninitialized value
by marinersk (Priest) on Apr 14, 2017 at 03:44 UTC
|
Okay, I'm playing around with this just for funz and learninz and I've hit something I'm having trouble explaining: Why does evalnot detect this as an error?
Original command placed in script and executed:
#!/usr/bin/perl
use strict;
use warnings;
print qq{perl version: $] \n};;;
my $x;
my %y= ("h"=>1, "i"=>2);
print $x, $y{x};
printf "%s", $x;
exit;
__END__
Produces, as advertised:
U:\>uninit01.pl
perl version: 5.018002
Use of uninitialized value $x in print at U:\uninit01.pl line 10.
Use of uninitialized value in print at U:\uninit01.pl line 10.
Use of uninitialized value $x in printf at U:\uninit01.pl line 11.
So I tried to capture the error in an evalblock:
#!/usr/bin/perl
use strict;
use warnings;
print qq{perl version: $] \n};;;
my $x;
my %y= ("h"=>1, "i"=>2);
eval
{
print $x, $y{x};
} ;
# Check for error out of eval{}
if ($@)
{
# There was an error trapped.
my $dsperr = $@;
print "-------------------------\n";
print "$dsperr\n";
print "-------------------------\n";
}
else
{
print "No error found.\n";
}
printf "%s", $x;
exit;
__END__
But this produced:
U:\>uninit02.pl
perl version: 5.018002
Use of uninitialized value $x in print at U:\uninit02.pl line 12.
Use of uninitialized value in print at U:\uninit02.pl line 12.
No error found.
Use of uninitialized value $x in printf at U:\uninit02.pl line 29.
What is it about evalthat I'm not understanding properly here?
| [reply] [d/l] [select] |
|
| [reply] [d/l] [select] |
|
Thank you -- That was the closest thing I could come up with, so I should trust my instincts more.
So --
Since you didn't mention one, I presume there isn't an evalequivalent which does catch warnings?
| [reply] [d/l] |
|
|
Re: determine the variable causing the error: Use of uninitialized value
by LanX (Sage) on Apr 14, 2017 at 13:44 UTC
|
| [reply] |
Re: determine the variable causing the error: Use of uninitialized value
by GrandFather (Saint) on Apr 23, 2017 at 10:58 UTC
|
It may help to trap the issue using a SIG handler. You can then do things like set a breakpoint in the handler and inspect variables, or add extra diagnostics to help track down the issue. Sig handlers look like:
$SIG{__WARN__} = \&DoWarn;
$SIG{__DIE__} = \&DoDie;
sub DoWarn {
my @params = @_;
warn @params;
return;
}
sub DoDie {
my @params = @_;
die @params;
}
Premature optimization is the root of all job security
| [reply] [d/l] |
Re: determine the variable causing the error: Use of uninitialized value
by karlgoethebier (Abbot) on Apr 14, 2017 at 14:56 UTC
|
"...I just want to know, when the error happens, which variable is not initialized correctly"
Probably it happens when you access the data and then it's too late ;-)
Kidding aside: Can you please provide some example data to ease the discussion? Most of this thread is based on assumptions about your data.
Regards, Karl
«The Crux of the Biscuit is the Apostrophe»
Furthermore I consider that Donald Trump must be impeached as soon as possible
| [reply] |
Re: determine the variable causing the error: Use of uninitialized value (don't pretend no warnings)
by Anonymous Monk on Apr 13, 2017 at 19:36 UTC
|
Hi,
In recent perls the warning will usually include the variable name
But its not like its a mystery of a hundred possibilities, its always narrowed down to one line
But since "debugging" is a hassle,
Knowing which variable it is not important, as are the warnings, so ignore it all
Don't pretend that you care when you don't :) its all voluntary
{
no warnings;
print '%s %s %s %s %s %s %s %s',
grep { defined $_ ? $_ : "" }
$a, $b, $c , $d ,$e, $f, $g, $h, $i, $j, $k , $l , $m , $n ,
+$o , $p , $q;
}
| [reply] [d/l] |
|
|