You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

File handles are traditionally package variables that represent file descriptors. Unlike other variables, file handles are typically not prefixed with punctuation. All barewords are subject to being interpreted by the parser differently than the developer intended, but bareword filehandles are particularly fraught with peril. Consequently, filehandles should never be stored as barewords.

Noncompliant Code Example

Suppose we maintain some simple code that makes the mistake of using bareword filehandles.

open( GOOD, "<", "good.txt");
my $good_data = <GOOD>;
print "GOOD: $good_data";
print "\n";
{
  open( BAD, "<", "bad.txt");
  my $bad_data = <BAD>;
  print "BAD: $bad_data";
  print "\n";
}
my $more_good_data = <GOOD>;
print "MORE GOOD: $more_good_data";

This code works as expected. It reads and prints a line of good text, followed by a line of bad text, followed by a second line of good text.

But during maintainance, someone (undoubtedly with the best of intentions) adds this function:

sub BAD {return GOOD;}

This completely changes the behavior of the subsequent code. The BAD bareword is now interpreted as a subroutine call, not a filehandle.

The program, as before, first opens good.txt, storing it in the GOOD filehandle, which is a package variable. It then proceeds to open bad.txt, but instead of storing the descriptor in a BAD filehandle, it stores the descriptor in the filehandle returned by the BAD() subroutine. Which returns GOOD. Consequently, the GOOD filehandle now points to the descriptor for bad.txt, not good.txt.

The program then tries to read from the BAD filehandle, but this produces nothing, since this filehandle was never actually opened. Nonetheless, the program then reads a line from the GOOD filehandle, and echoes it. Which turns out to be from bad.txt, rather than good.txt.

Compliant Solution

This compliant solution protects the file descriptors by using anonymous scalars rather than bareword filehandles.

sub BAD {return GOOD;}

open( my $GOOD, "<", "good.txt");
my $good_data = <$GOOD>;
print "GOOD: $good_data";
print "\n";
{
  open( my $BAD, "<", "bad.txt");
  my $bad_data = <$BAD>;
  print "BAD: $bad_data";
  print "\n";
}
my $more_good_data = <$GOOD>;
print "MORE GOOD: $more_good_data";

Consequently, the original behavior of this program is restored. Because the $BAD variable is declared with my it is a lexical variable, rather than a package variable. So it is unaffected by the BAD subroutine. So this program once again prints two lines from the good.txt file, and one from the bad.txt file, and never confuses the two.

Exceptions

FIO00:EX0: According to [CPAN/PerlCritic]:

There are three exceptions: STDIN, STDOUT and STDERR. These three standard filehandles are always package variables.

These bareword filehandles may be used.

Risk Assessment

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

FIO00-PL

medium

probable

low

P12

L1

Automated Detection

Tool

Diagnostic

Perl::Critic

InputOutput::ProhibitBarewordFileHandles

Bibliography

[Conway 05] pg. 202 "Filehandles"
[Wall 2011] perlfunc
[CPAN] Elliot Shank, Perl-Critic-1.116 InputOutput::ProhibitBarewordFileHandles


  • No labels