Monday, May 4, 2009

Don't forget the forgotten perl pragmas...

In the past 10 years, I've seen countless people banging their heads over why their perl code is not functioning correctly. When I ask about using the strict and warnings pragma, either they stare at me with a blank expression - or their incorrect usage of the modules loses the purpose...

Why 'warnings'?

Add this to the top of your perl package or script:

use warnings;

This will import the warnings module into your namespace and, if you have some mistakes, it will tell you about them.... What mistakes you might ask? For starters, it will tell you about a few typo conditions - where you use a variable but don't do anything with it. Example:
    #!/usr/bin/perl
use warnings;
$filename = "/tmp/foo";
print "hello world\n";
This will throw a warning like this: 'Name "main::filename used only once: possible typo at ...' nice eh? There's more! The warnings pragma will also tell you if you do something silly like this:
    #!/usr/bin/perl
use warnings;
$filename = "/tmp/foo";
open(FILE,"w");
close(FILE);
print FILE, "Hello world\n";
Oops! That print statement is not in the right spot - perl will remind you gently by warning you that you're trying to write to a closed file handle.. Sure it will continue, but now you know why there is no data in your file!

Now comes my favorite - the "uninitialized value" warning - This code doesn't work:
    #!/usr/bin/perl
use warnings;
print "I need " . $something . "\n";
$something = "help";
Why? Because $something has no value - it was never initialized prior to use. In this case, it is NULL or otherwise `undef` (undefined). Some people prefer to test `if ($something eq '')` - but that's just wrong (since '' and undef are two separate values!). Instead, do it right - call `if (defined($something) && $something eq '')`.

Last on the warnings pragma - if typing 'use warnings;' is too much for you, then prefix your perl code in one of the following manners:
    #!/usr/bin/perl -w
#!/usr/bin/perl -W
Lower-case w will import 'warnings' into your package. '-W' will import warnings into all packages.

So now you have all of these lovely warnings printed on your screen - what about the strict pragma? Add this to the top of your perl package or script:
    use strict;
This will import the strict module into your namespace and, if you have mistakes - it will tell you about them... (Sound familiar?!). This time, rather than just uninitialized variables, `strict` will help you keep the right scope in your references, variables and subroutines. Simply put, you need to make sure you properly scope your variables. For instance:

#!/usr/bin/perl -w
use strict;

my $bar = 'something';
&changeBar('else');
print $bar . "\n";
sub changeBar {
my $val = shift;
$bar = $val;
}

What does this code produce? It prints 'else' instead of 'something'. Simple enough, right? Far too many people miss the basics here, ignore that their variables need to be scoped - and then wonder why their counters aren't working. Maybe this example is a little better:
    #!/usr/bin/perl
$count = 0;
&a;
print "Count: " . $count . "\n";
sub a {
for ($x=0;$x<100;$x++) {
print "a: " . $x . "\n";
&b;
print "now: " . $x . "\n";
}
}
sub b {
for ($x=200;$x>150;$x--) {
$count++;
}
}

What does this produce? It may not be the result you desire:
    [root@delta ~]# ./nostrict.pl
a: 0
now: 150
Count: 50
[root@delta ~]#
What did the user want? Probably to have $count be 5000. How could this have been resolved? In each subroutine, scope $x by declaring them with 'my' either:
    my $x;
or
for (my $x; ....)

That's it - then, your variables are scoped a little better, and you're not banging your head on the wall for something silly.

For anyone looking to put together any code shy of the 1-liner, using 'warnings' and 'strict' is a big MUST.

In conclusion - save yourself from headache -
use warnings;
and
use strict;
- they may save your $life one day...

References:
http://search.cpan.org/perldoc?warnings
http://search.cpan.org/perldoc?strict

0 comments:

Post a Comment