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

G'Day Monks,

I'm working on some code that involves some heavy file I/O. There will be lots of open() and close() calls. The kicker is that every time I mess with file, there are going to be some other things I'm required to do (examples: file locking, reporting what files were opened when, notifying someone via some method, etc). So what I would like to do is create a subroutine that handles everything in one place. E.g. something like:
sub myopen { my ($file, $mode, $locktype) = @_; #open the file...for the sake of brevity #I'll not include the mode logic here if (open FH, "$file"){ print "Opened $file successfully.\n"; #lock the file #do whatever else needs to be done return *FH; } else { warn "Can't open: $!\n"; } }
That way any time I need to open a file, I can just call on this sub instead of doing open(), then flock(), then whatever else every single time.

The trouble is that this is broken. =) I wrote a simple little test script that just opens a couple of handles using something like the above. When I close any one of the handles, I suddenly get "Bad file descriptor" when trying to do anything with the other. I think you can see why. There's a duplicate handle name problem and a probably a scoping issue ("use strict" is a requirement for this code).

So is there a way to do this that actually works? Just off the top of my head, I wonder if it would be possible to do something like create a handle that is not associated with any file, pass that to the sub and have the sub use it in the open() call. But any solution would do.

Thanks!

Replies are listed 'Best First'.
Re: Unassociated Filehandles and Subroutines
by JediWizard (Deacon) on Sep 21, 2004 at 15:43 UTC

    You can use a lexically scoped scalar as your file handle and return it. This will prevent you from overwriting one open filehandle with another. I would advise always using lexically scoped scalars for file handles (it is just generally safer). See the below example:

    sub myopen { my ($file, $mode, $locktype) = @_; #open the file...for the sake of brevity #I'll not include the mode logic here if (open(my $fh, "$file")){ print "Opened $file successfully.\n"; #lock the file #do whatever else needs to be done return $fh; } else { warn "Can't open: $!\n"; } }
    May the Force be with you
Re: Unassociated Filehandles and Subroutines
by ikegami (Patriarch) on Sep 21, 2004 at 15:48 UTC

    The solutions the others posted are great, but they didn't explain why, or at least not clearly. The problem is that you're using the same file handle (FH) for all of your files, so when you try to close two files, you end up closing the same one twice.

    btw, another way of doing it is to use the FileHandle module:
    my $fh = FileHandle->new('filename', 'r') or die(...);

    There is another problem with your code. When you have an error, you're returning the return value of warn, whatever that is. You should probably either die, or return undef.

      The problem is that you're using the same file handle (FH) for all of your files, so when you try to close two files, you end up closing the same one twice.

      Actually, each time an unlocalized global file handle is opened, the previously attached file is closed. One name, one handle.

      After Compline,
      Zaxo

Re: Unassociated Filehandles and Subroutines
by rjbs (Pilgrim) on Sep 21, 2004 at 15:36 UTC
    Consider avoiding the ALL CAP FILEHANDLE in favor of lexical filehandle references. They'll go out of scope and auto-close when they're no longer referenced, they help you avoid dealing with globs, and they just look better in your code, frankly.

    consult perldoc -f open and (better) perldoc perlopentut
    rjbs
Re: Unassociated Filehandles and Subroutines
by Fletch (Bishop) on Sep 21, 2004 at 15:36 UTC

    You need to look at IO::File, or open( my $fh, ... ) for newer perls (see perldoc perlopentut).

Re: Unassociated Filehandles and Subroutines
by BrowserUk (Patriarch) on Sep 21, 2004 at 17:14 UTC

    The simplest modification to your routine is

    sub myopen { my ($file, $mode, $locktype) = @_; local *FH; ## Ensure that we get a new GLOB each time #open the file...for the sake of brevity #I'll not include the mode logic here if (open FH, "$file"){ print "Opened $file successfully.\n"; #lock the file #do whatever else needs to be done return *FH; } else { warn "Can't open: $!\n"; } }

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: Unassociated Filehandles and Subroutines
by shemp (Deacon) on Sep 21, 2004 at 15:55 UTC
    This looks like the job for an object. I think the specific problem of the Bad File Descriptor has already been answered. When you need something like this, where you want to extend the functionality of something (a filehandle here), that screams object to me.