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

I need to find a way to manipulate a filehandle as a variable.

I know (or believe I know) several things

  1. Filehandles are scoped the same way variables are
  2. Despite this similarity, you can't assign filehandles in the same way you do variables, because that reads from the handle instead

My problem is that I want to open a file inside a subroutine, but have the resulting filehandle be available for use everywhere after the function is called. For example...

#example 1 HANDLE = &callafunction(); # what is HANDLE? I don't know print HANDLE "This prints to file.txt\n"; sub callafunction { open ( OUT, "file.txt" ) or die ( "open no worky!: $!" ); # somehow "return" OUT so it gets aliased to HANDLE }

Also, I may need to open several files in this subroutine, so I would like to be able to put these "file-handle-variables" into an array of some kind, and then return that like this...

#example 2 ( HANDLE1, HANDLE2 ) = &callafunction(); print HANDLE1 "This goes to file1.txt\n"; print HANDLE2 "This goes to file2.txt\n"; sub callafunction { open HANDLE1, "file1.txt" or die ( "open no worky!: $!" ); open HANDLE2, "file2.txt" or die ( "open no worky!: $!" ); my @array; # somehow push HANDLE1 and HANLDE2 onto @array @array; }

I used two examples because it occurred to me that it might be possible to do one of these tasks, but not the other (or someone might know the answer to one, and not the other).

Does anyone know how I can go about these tasks, if either of them are possible?

Thanks in advance.

Replies are listed 'Best First'.
Re: returning filehandles
by chipmunk (Parson) on Jan 31, 2001 at 00:48 UTC
Re (tilly) 1: returning filehandles
by tilly (Archbishop) on Jan 31, 2001 at 01:35 UTC
    I don't know why people are telling you to use the IO modules. Personally I don't like the IO modules. Just remember that you have to actually put the filehandle into a variable before reading from it, and it is actually quite simple to have an array of filehandles. (If you use something more complex Perl will get confused and think it is a glob pattern or an expression.) See RE (tilly) 1: Merging files for a working example that does something useful.

    Warning, there is usually a hard limit on how many filehandles you may have open. Often it is only a few hundred. Caveat programmer.

    Trivia, in there I use Symbol so I can call gensym(). If you don't want to use the magic call to gensym(), on 5.6 the handle will create itself upon demand (ie it autovivifies), and in earlier Perl's you can do this:

    my $fh = do {local *FOO};
    to get the same effect.
Re: returning filehandles
by Fastolfe (Vicar) on Jan 31, 2001 at 00:48 UTC
    It might make things easier on you if you used IO::File and/or IO::Handle to get real scalars that are functionally filehandles. Then you can pass things around just like any other scalar, assign them, etc.

    Otherwise, try using typeglob assignments (e.g. *FILEHANDLE = whatever).

Re: returning filehandles
by DeusVult (Scribe) on Jan 31, 2001 at 01:25 UTC
    My comments will be in two parts, each regarding a different possible suggested solution.
    IO::File, For that Matter, Modules in General
    1. How do I know if this module is installed?
    2. If it isn't installed and I get it from CPAN, how do I install it?
    3. The IO::File node shows how to open, close, and read from a IO::File object, but not how to write to one opened for writing. Where can I get more complete information?
    Typeglobs
    The filehandle and passing stuff nodes seems to imply that once such a typeglob is declared in a subroutine it must be passed in to every function that wishes to use it. So I have to do it like this?
    my *bighandle = openafile(); useafile(*bighandle); sub openafile { local *handle; open handle, "file" or die; return *handle; } sub useafile { local *handle = shift; # do stuff with *handle }
    This is a little inconvenient if I want to use this handle everywhere. Which leads me to a scoping question. Since I declared my *bighandle; at the global level, will *bighandle be visible in useafile even if I don't pass it in? How do you force a variable to be global?

    Thanks again

    Some people drink from the fountain of knowledge, others just gargle.

      1. How to tell if a module is installed: perldoc Module or perl -MModule -ce 1
      2. How to install a module from CPAN: use the CPAN module (perl -MCPAN -e shell) or follow the instructions that come with the downloaded module.
      3. How to write to a filehandle opened for writing: the IO::File node here on PerlMonks appears to be corrupted. Use perldoc to read the documentation on your local system; it does include examples of writing to an IO::File object.

      Regarding your code question... Globs cannot be declared with my. However, if you change the first line to: *bighandle = openafile(); then everything in the same package can access bighandle directly. (And code in other packages can access it as, for example, main::bighandle.)

      Re: the first set of questions:

      1a. perl -M[MODULE NAME] -e '1' will die with an error message.
      1b. IIRC, IO::File and members of its clan are standard with recent versions of Perl (v. >= 5.00503?)

      2. You can open a file for writing by passing "> [filename]" to the constructor. You then pass the object thus created around.

      3. The fine manual is for reading ... but the overall best way is to configure the CPAN module and it will do most things for you automagically, i.e. once it's configured, a simple perl -MCPAN -e 'install [MODULE NAME]' will suffice for most modules.

      Philosophy can be made out of anything. Or less -- Jerry A. Fodor