Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl warning: Use of uninitialized value in join or string

Tags:

warnings

perl

dbi

I'm getting:

"Use of uninitialized value in join or string at ./test_script.pl line 69, <fh> line 91."

The code creating this warning is here:

# Write results of SQL execution to output file
if ($@) { print "\n DB Error : ", $@; exit; }
open(FH, ">$output_file") or 
        die "\n cannot write to the file $output_file. Check the file permissions";
while(my @row= $sth->fetchrow_array) {
      print FH join($delimiter, @row). "\n";
}

It is coming from the "print ... join ...." line in the "while" block, but I'm not sure 100% on what is causing it and how to rectify it.

The $delimiter is initialized earlier as my $delimiter = "|" and the @row is getting its values from the $sth operation in the previous line.

Is this a "timing" issue regarding the @row values or is it something in the actual SQL query file being processed, as indicated by the "< fh> line 91" part of the warning? Any ideas on how I can avoid the warnings? BTW, the script works fine, it just generates a warning for each @row value fetched, which creates a huge amount of warnings on a large data set pull...

like image 679
Vas Vas Avatar asked Nov 06 '14 17:11

Vas Vas


1 Answers

@row may contain some elements that have an undefined value, probably because one or more of the columns in the database that populated @row was NULL. The behavior can be mimicked while still using join in a simple Perl one-liner like this:

perl -we 'my @array = ("Hello",undef,"world!"); print join("|",@array) . "\n";'

The output that will produce is:

Use of uninitialized value $array[1] in join or string at -e line 1.
Hello||world!

The reason this happens is because join is trying to join a value that doesn't stringify without being upgraded from "undefined" to "defined". Stringifying undef produces this warning message to alert you that you may be doing something unintentional, and incorrect.

An even simpler example could be this:

$ perl -we 'my $scalar; print "$scalar\n";'
Use of uninitialized value $scalar in concatenation (.) or string at -e line 1.

...or...

perl -wE 'say "" . undef'

Again, we get the warning because we're interpolating an undefined value into a string (in the first case), or forcing stringification (in the second case). Interpolation causes stringification, and stringification of an undefined value generally throws a warning to let you know you may have made a mistake.

If you don't mind silently upgrading the undef's to an empty string, you could do this:

print FH join( $delimiter, map { defined ? $_ : '' } @row ) . "\n";

This pre-processes @row before handing it to join(). For each element in @row, if the element has an undefined value, that value is replaced by an empty string, which is defined but visually quiet.

The other possibility is that $delimiter is undefined. Test for that by printing it, and if that turns out to be the case, the steps you will need to take to fix it are to simply make sure that it actually contains a value.

Simply silencing the warnings in a tight lexical scope could be ok as well, though from a philosophical standpoint, one method removes the cause, and the other removes one of the symptoms. The "no warnings 'uninitialized'; method described elsewhere does just that; silences the warning. You would do this when you know you're doing something perfectly Ok, but you also know that Perl is going to warn you about it because many times it's a mistake. This could be such a situation; you may be perfectly happy with just letting silent stringification of an undefined value happen inside of the join. And if that's the case, go ahead and squelch the warning. The most important thing is that you know what it means, verify what is causing it, and then make an informed decision as to what to do about it.

like image 73
DavidO Avatar answered Oct 20 '22 11:10

DavidO