Perl FAQ 5.25: How can I use pass a filehandle to a function, or make a list of

Perl FAQ 5.25

How can I use pass a filehandle to a function, or make a list of

filehandles?

If you've ever tried to use a variable for a filehandle, you may well have had some problems. This is just revealing one of the icky places in perl: filehandles aren't first-class citizens the way everything else is, and it really gets in the way sometimes.

Of course, it's just fine to say

    $fh = "/some/path";
    open($fh, "< $fh");
    print $fh "string\n";

But you'll still get into trouble for trying:

    $fharray[$i] = "/some/path";
    open($fharray[$i], "< $fharray[$i]");
    print $fharray[$i] "stuff\n";

You can also do this:

    $tmp_fh = $fharray[$i];
    print $tmp_fh "stuff\n";

But this is ok:

    print { $fharray[$i] } "stuff\n";

There are about four ways of passing in a filehandle. The way everyone tries is just to pass it as a string.

    printit(Some_Handle);

Unfortunately, that doesn't work so well, because the package that the printit() function is executing in may in fact not be the one that the handle was opened it:

A simple fix would be to pass it in fully qualified;

    printit(main::Some_Handle);

because if you don't, the function is going to have to do some crazy thing like this:

CODE 1:

    sub printit {
        my $fh = shift;
        my $package = (caller)[0];
        $fh =~ s/^[^':]+$/$package::$&/;
        while (<$fh>) {
            print;
        } 
    } 
     
    A better solution is to pass a typeglob instead:

CODE 2:

    printit(*Some_Handle);
    sub printit {
        local *FH = shift;
        while () {
            print;
        } 
    }

However, it turns out that you don't have to use a typeglob inside the function. This also works:

CODE 3:

    printit(*Some_Handle);
    sub printit {
        my $fh = shift;
        while (<$fh>) {
            print;
        } 
    }

As does this even, in case you want to make an object by blessing your reference:

CODE 4:

    printit(\*Some_Handle);
    sub printit {
        my $fh = shift;
        while (<$fh>) {
            print;
        } 
    }

I used to think that you had to use 1 or preferably 2, but apparently you can get away with number 3 and 4 as well. This is nice because it avoids ever assigning to a typeglob as we do it 2, which is a bit risky.

Some other problems with #1: if you're using strict subs, then you aren't going to be able to do that: the strict subs will gets you. Instead, you'll have to pass in 'main::Some_Handle', but then down in your function, you'll get blown away by strict refs, because you'll be using a string a symbol. So really, the best way is to pass the typeglob (or occasionally a reference to the same).


Other resources at this site: