http://qs1969.pair.com?node_id=1187864

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.

Replies are listed 'Best First'.
Re: determine the variable causing the error: Use of uninitialized value
by haukex (Archbishop) 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)

      ... which version of Perl are you using?

      ruqui: This is an important question. The boon you seek was added with Perl version 5.10:

      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:  <%-{-{-{-<

        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>
Re: determine the variable causing the error: Use of uninitialized value
by LanX (Saint) 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!

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      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.
        ... 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:  <%-{-{-{-<

        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)

        If it's undefs in a Perl data structure that you seek...

        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}
        OK I was able to reproduce (some of) your problems in 5.14 ... that's indeed pretty inconsistent.

        As a first step we should check if newer versions still have these problems.

        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.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        "...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

Re: determine the variable causing the error: Use of uninitialized value
by haukex (Archbishop) 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. <>
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?

      ... uninitialized value ... is a warning, not an error (unless it's escalated to FATAL-ity); no exception is thrown.


      Give a man a fish:  <%-{-{-{-<

        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?

Re: determine the variable causing the error: Use of uninitialized value
by LanX (Saint) on Apr 14, 2017 at 13:44 UTC
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
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

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; }