Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks

I am reading the last second line from a log file, my question is how to avoid "Use of uninitialized value" when the file I am reading/open is empty.

Here is some code to show where its happening :
... # Read the last 2 lines of a log file my $log = "log.log"; # if this file is still empty by the time the co +de runs. my $line = do { open my $line, '-|', tail => -2, $log or die "Can't spawn tail: $!\n"; <$line>; }; $line =~ s/[\r\n]+$//; my $show = ''; # <<<< happens here, uninitial +ized value if ($line =~ /(\bTest\b\s+.+\s+\baccount\b)?/) { #<<<< happens here, + uninitialized value $show = $1 || ''; } ...

Thanks for looking!

Replies are listed 'Best First'.
Re: Preventing Use of uninitialized value.
by 1nickt (Canon) on Dec 12, 2018 at 18:58 UTC

    Don't test for a match if there is no line:

    if (defined $line and $line =~ /(\bTest\b\s+.+\s+\baccount\b)?/)
    if ( ($line // '') =~ /(\bTest\b\s+.+\s+\baccount\b)?/)
    (Update: added parens to handle precedence of //, thx to choroba)

    Hope this helps!


    The way forward always starts with a minimal test.
      Unfortunately, // has a different precedence:
      if (($line // '') =~ /(\bTest\b\s+.+\s+\baccount\b)?/)

      Easy to verify with B::Deparse:

      $ perl -MO=Deparse,-p -e 'if ($line // "" =~ /(\bTest\b\s+.+\s+\baccou +nt\b)?/) {}' if (($line // ('' =~ /(\bTest\b\s+.+\s+\baccount\b)?/))) { (); } -e syntax OK

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        Yep! As you were writing that I was composing another reply in which I discovered that fact.

        Thanks for the correction.


        The way forward always starts with a minimal test.
Re: Preventing Use of uninitialized value.
by Laurent_R (Canon) on Dec 12, 2018 at 23:37 UTC
    At the very least, check for the file size before reading it.
Re: Preventing Use of uninitialized value.
by hippo (Archbishop) on Dec 12, 2018 at 23:49 UTC

    It rather depends what you want your code to do instead. Since you are happy for it to die if you can't tail it then let's carry on throwing those exceptions.

    die "File $log isn't long enough to process\n" unless defined $line;
Re: Preventing Use of uninitialized value.
by tobyink (Canon) on Dec 12, 2018 at 21:27 UTC
    use strict; use warnings; no warnings qw(void once uninitialized); # the annoying warnings

      Hey tobyink, it does rectify the OPs question, but it appears as though you may be being a bit facetious here. Yeah?

        Not really. Those warnings annoy me.

        The ability to treat an uninitialized variable as zero or the empty string is handy and is part of what makes perl Perl. Explicitly having to check whether a variable is undef at runtime makes code unnecessarily verbose and (very slightly) slower. I can understand wanting to have this warning available to help people with debugging, but 99% of the time, it's annoying.

        I don't think the once or void warnings categories have ever helped me find a real error in my code. once becomes annoying if you want to do things like:

        printf "%s%s\n", $MyModule::Config::Indent ? "\t" : "", $output;

        Where $MyModule::Config::Indent is a variable that end users can set to true if they want indented output. That might be the only place in my module where I perform any output, so that variable would only be referenced once. I don't want a warning for that. (And yes, I know globals are bad in general, but there are some places where their use can be expedient.)

        The numeric warnings category can be annoying at times too, but it's just helpful enough for me to want to keep it enabled most of the time.