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

I managed to get caught out the other day writing something that amounted to the following code.
#!/usr/bin/perl -w use strict; open (IN, "file1") or die("can't blah blah"); while (<IN>) { print; print slurp(); } sub slurp { my $slurp = do { local $/ = undef; open IN, "file2"; <IN>}; return $slurp; }
Can you spot it? Yeah, of course you can, but my code was a bit more involved than this ;). To clarify, what happens is as follows. The first 'while' successfully reads the first line of file1. It prints this line, then the slurp returns file2, and its contents are also printed. Unfortunately, because I used the same file handle, the while thinks its at the end of file1 and and skips to the end.

Okay fair enough, filehandles appear to be global, my stupid mistake, but my question is this...

Can I avoid ever making this mistake again?

Should I always 'use FileHandle'? Is this a bit over the top, or worth the effort? It would have been nice if I'd been given some warning too. :)

I'd be interested to hear if others have been caught out by this, and what you're doing now to avoid the same.

---
my name's not Keith, and I'm not reasonable.

Replies are listed 'Best First'.
Re: Filehandles, scope and warnings.
by Perl Mouse (Chaplain) on Nov 22, 2005 at 11:51 UTC
    Can I avoid ever making this mistake again?
    Yes.
    Should I always 'use FileHandle'?
    No, all you need to do is get on with the program. Using typeglobs as filehandles is so perl4ish, it hasn't been needed since perl 5.000, although it really has been perl 5.6 that made using refs to filehandles much easier. And perl 5.6 dates from early spring 2000.
    use strict; use warnings; open my $in, "file1" or die "..."; while (<$in>) { print; print slurp(); } sub slurp { my $slurp = do {local $/; open my $in, "file2" or die; <$in>} return $slurp; }
    Perl --((8:>*
Re: Filehandles, scope and warnings.
by davorg (Chancellor) on Nov 22, 2005 at 11:52 UTC

    The "old" way would be to localise the glob.

    open (IN, "file1") or die("can't blah blah"); while (<IN>) { print; print slurp(); } sub slurp { local *IN; my $slurp = do { local $/ = undef; open IN, "file2"; <IN>}; return $slurp; }

    But these days you can use lexical filehandles.

    open (my $in, "file1") or die("can't blah blah"); while (<$in>) { print; print slurp(); } sub slurp { my $slurp = do { local $/ = undef; open my $in, "file2"; <$in>}; return $slurp; }

    Or, as you point out, you can use FileHandle or IO::File.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: Filehandles, scope and warnings.
by Moron (Curate) on Nov 22, 2005 at 17:24 UTC
    Apart from using 'my', there seem to be other good habits to get into from reading the OP:

    - using close,

    - avoiding unecessary loops that repeat the same functionality - for example the OP code opens and reopens the same file and slurps it for each line of another file.

    - if the container for the filehandle goes out of scope inconveniently, just transfer it to one that does have adequate scope.

    Or to put that all into code:

    #!/usr/bin/perl use strict; use warnings; open my $fh1, "file1" or die "$!: file1"; my $fh2; my @file2; { local $/ = undef; open my $fh, "file2" or die "$!: file2"; $fh2 = $fh; @file2 = <$fh>; } close $fh2; # could have closed in the above block, but just illustrat +ing a point about scope here while (<$fh1>) { print; print @file2; } close $fh1;

    -M

    Free your mind