Приглашаем посетить
Мережковский (merezhkovskiy.lit-info.ru)

Section 10.1.  Filehandles

Previous
Table of Contents
Next

10.1. Filehandles

Don't use bareword filehandles.

One of the most efficient ways for Perl programmers to bring misery and suffering upon themselves and their colleagues is to write this:

    open FILE, '<', $filename
        or croak "Can't open '$filename': $OS_ERROR";

Using a bareword like that as a filehandle causes Perl to store the corresponding input stream descriptor in the symbol table of the current package. Specifically, the stream descriptor is stored in the symbol table entry whose name is the same as the bareword; in this case, it's *FILE. By using a bareword, the author of the previous code is effectively using a package variable to store the filehandle.

If that symbol has already been used as a filehandle anywhere else in the same package, executing this open statement will close that previous filehandle and replace it with the newly opened one. That's going to be a nasty surprise for any code that was already relying on reading input with <FILE>[*].

[*] Not that we should have too much sympathy for that code, as it's behaving just as badly by using the FILE bareword itself.

The writer of this particular code also chose the imaginative name FILE for this particular filehandle. That's one of the commonest names used for package filehandles[Section 10.1.  Filehandles], so the chances of colliding with someone else's open filehandle are greatly enhanced.

[Section 10.1.  Filehandles] The other Four Horsemen of the I/O-pocalypse being IN, OUT, FH, and HANDLE.

As if these pitfalls with bareword filehandles weren't bad enough, barewords are even more unreliable if there's a subroutine of the same name currently in scope. And worse still, under those circumstances they may fail silently. For example:

    
    # Somewhere earlier in the same package (but perhaps in a different file)...
    use POSIX;

    # and later...

    # Open filehandle to the external device...
    open EXDEV, '<', $filename
        or croak "Can't open '$filename': $OS_ERROR";

    # And process data stream...
    while (my $next_reading = <EXDEV>) {
        process_reading($next_reading);
    }

The POSIX module will have quietly exported a subroutine representing the POSIX error-code EXDEV into the package's namespace (just as if that constant had been declared in a use constant pragma). So the open statement is really:

    open EXDEV(  ), '<', $filename
        or croak "Can't open '$filename': $OS_ERROR";

When that statement executes, it will first call the EXDEV( ) subroutine, which happens to return the value 18. The open statement then uses that value as a bareword filehandle name, opens an input stream to the requested file, and stores the resulting filehandle in the package's *18 symbol table entry[Section 10.1.  Filehandles].

[Section 10.1.  Filehandles] Yes, it's a valid symbol name: the regex capture variable $18 lives there.

Unfortunately, the EXDEV( ) subroutine isn't visible within the angle brackets of a subsequent input operation (i.e., <EXDEV>), because the input operator always treats an enclosed bareword as the direct name of the package filehandle that it's supposed to read from. As a result, the angle brackets attempt to read from *EXDEV, which results in a completely accurate, but highly confusing error message:

    readline(  ) on unopened filehandle EXDEV

The usual conundrum at that point is: how can the filehandle possibly be unopened, when the open statement on the immediately preceding line didn't throw an exception??? And if the obvious culprit (the use POSIX) is off in another file somewhere, it can be very difficult to track down what's going wrong.

Curiously, the code would work as intended if it were rewritten like so:

    
    # And process data stream...
    while (my $next_reading = <18>) {
        process_reading($next_reading);
    }

But that's hardly an ideal solution. The ideal solution is not to use bareword filehandles at all.

    Previous
    Table of Contents
    Next