Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

desire to pass file handles by value

by mandog (Curate)
on Nov 07, 2003 at 02:22 UTC ( #305217=perlquestion: print w/ replies, xml ) Need Help??
mandog has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to pass file handles to subs by value. The FAQ gave me a recipe but the position of the file gets changed.

#!/usr/bin/perl -w use strict; use 5.6.1; sub find { local *FH = shift; my $find_me = shift; my $count = 0; my $tell = tell(FH); while (<FH>) { $count++ if (/$find_me/); } return "count: $count tell: $tell\n"; } open OUT, '>', '/tmp/tmp.txt' or die "$!\n"; print OUT while (<DATA>); close OUT or die "$!\n"; open IN, '/tmp/tmp.txt' or die "$!\n"; print find( *IN, 'a' ); print find( *IN, 'a' ); print find( *IN, 'd' ); print find( *IN, 'e' ); #count: 3 tell: 0 #count: 0 tell: 13 #count: 0 tell: 13 #count: 0 tell: 13 __DATA__ a a a c d e

Comment on desire to pass file handles by value
Download Code
Re: desire to pass file handles by value
by Roger (Parson) on Nov 07, 2003 at 02:42 UTC
    I think you *need* to rewind the file by calling seek FH, 0, 0; in the subroutine find.

    The file operation in Perl is ultimately linked to the underlying unix file handles. When you read from a Perl file handle, Perl uses and updates the underlying unix file handle, thus giving the side effect.

    I think ideally you are looking for the Perl's equivalent to the C stdio's dup function, to duplicate/copy an existing file handle into a new file handle (not just an alias to the existing file handle, but a new independent file handle), and operate on the second file handle in the subroutine. I am not aware of such facility in Perl 5 (my lack of research perhaps), but I think Perl 6 has implimented the dup function for the File Object.

    Update:
    Ok, I just learned that you can duplicate a file handle in Perl with open NEW, "<&OLD";. I quickly came up with the following code -

    #!/usr/bin/perl -w use strict; sub find { local *IN = shift; my $find_me = shift; my $count = 0; # duplicate existing file handle open F2, '<&', *IN or die "Can not duplicate file handle"; my $tell = tell(F2); while (<F2>) { $count++ if (/$find_me/); } return "count: $count tell: $tell\n"; close F2; } open OUT, '>', 'tmp.txt' or die "$!\n"; print OUT while (<DATA>); close OUT or die "$!\n"; open IN, 'tmp.txt' or die "$!\n"; print find( *IN, 'a' ); print find( *IN, 'a' ); print find( *IN, 'd' ); print find( *IN, 'e' ); __DATA__ a a a c d e
    The output is still -
    #count: 3 tell: 0 #count: 0 tell: 13 #count: 0 tell: 13 #count: 0 tell: 13
    That didn't work either! Ok, that taught me a lession - my assumption on the duplicated file handle could be wrong. I need some re-education.

    Fellow monks, could you please tell me what is wrong with the duplicated file handle? Is perl actually creating a second independent file handle? Am I doing the right thing at all?

    Thanks!

      Thanks for the help. For now, I'm going to record the orignal file position on entry to the sub & restore it on exit.

      #... my $pos=tell(FH); #... seek(FH,$pos,0); return

      The file handle duplicating thing looks interesting though...

      update: fixed dumb semantic error

        Now if only you could localize a variable that represents the file position, you wouldn't have to manually save/restore its value. Hmmm....
        package Tie::Scalar::FHPos; use FileHandle; sub TIESCALAR { my( $pkg, $fh ) = @_; bless [$fh], $pkg; } sub FETCH { my( $self ) = @_; $self->[0]->tell } sub STORE { my( $self, $pos ) = @_; $self->[0]->seek($pos,0); }
        Test:
        open F, "< foo.dat"; our $pos; tie $pos, 'Tie::Scalar::FHPos', \*F; { local $pos = 20; print "$pos: ", scalar(<F>); } print "$pos: ", scalar(<F>);
        Cool!

        jdporter
        The 6th Rule of Perl Club is -- There is no Rule #6.

      The file operation in Perl is ultimately linked to the underlying unix file handles. When you read from a Perl file handle, Perl uses and updates the underlying unix file handle, thus giving the side effect.
      Well not on windows, or other non-unix operating systems ....
      Update: Ok, I just learned that you can duplicate a file handle in Perl with open NEW, "<&OLD";. I quickly came up with the following code -

      From a Linux man page...

      After successful return of dup or dup2, the old and new descriptors may be used interchangeably. They share locks, file position pointers and flags; for example, if the file position is modified by using lseek on one of the descriptors, the position is also changed for the other.
      --
      James Antill
Re: desire to pass file handles by value
by pg (Canon) on Nov 07, 2003 at 03:12 UTC

    Besides what Roger said, if this is all what you want to do, you can:

    use Data::Dumper; use strict; use warnings; my @lines = <DATA>; my $count = {}; map {chomp;$count->{$_} ++} @lines; print Dumper($count); __DATA__ a a a c d e

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://305217]
Approved by jdtoronto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2014-08-22 23:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (168 votes), past polls