Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Passing a filehandle to subroutine

by Kagami097 (Initiate)
on Aug 10, 2017 at 15:58 UTC ( #1197186=perlquestion: print w/replies, xml ) Need Help??
Kagami097 has asked for the wisdom of the Perl Monks concerning the following question:

Hello All, I am facing an issue while passing an open filehandle to a subroutine .The code goes something like below.

my $in = "class.txt"; open F , $in or die; my $line =<F>; while($line){ if ($line =~ /xyz/){ SubOut(\*F); } $line =<F> ; } close F; sub SubOut{ my $fh = shift; my $s_line = <$fh>; my @ref; push (\@ref, $s_line); while($s_line){ print STDOUT $s_line; $s_line = <$fh>; } }
When i execute this code , i end up losing the line that matches in the main program.How can i pass an open filehandle to a subroutine and store the line that matches in the main program.In the code above i am losing that line cause of my $s_line = <$fh>.

Replies are listed 'Best First'.
Re: Passing a filehandle to subroutine
by hippo (Abbot) on Aug 10, 2017 at 16:17 UTC

    Here is a simplified SSCCE which hopefully shows that your SubOut as written will exhaust the passed filehandle. If that isn't what you want it to do then you will need to change the algorithm in SubOut().

    use strict; use warnings; use Test::More tests => 4; open (F, "echo -e 'a\nb\nc'|"); my $line = <F>; is ($line, "a\n", "a in main()"); while ($line) { if ($line =~ /a/) { SubOut (\*F); } ok (eof (F), "F is exhausted"); $line = <F>; } close (F); sub SubOut { my $fh = shift; my $s_line = <$fh>; while ($s_line) { like ($s_line, qr/[bc]\n$/, "b or c matched in SubOut()"); $s_line = <$fh>; } }

    Note, this was tested on Linux. Results on non-unixy platforms may differ. Caveat emptor.

Re: Passing a filehandle to subroutine
by Marshall (Abbot) on Aug 10, 2017 at 22:58 UTC
    A few comments:
    • The file handle "F" has package scope and actually would be known in the subroutine without passing it as a parameter. I recommend using lexically scoped "my" variables for filehandles as I show below.
    • If for some reason the subroutine needs to know the line that "triggered its being called", just pass that line to the sub as I also show. There are complicated ways to "re-read" and otherwise deal with "backing up one line" on a filehandle, but usually the best way is to just tell the subroutine that line as a parameter so that nothing complicated is required. In general once you read a line from a filehandle, use it as soon as a possible, or save it, but don't "back-up" in the input stream of lines from the filehandle.
    • I show the standard idiom for a while{} loop that reads a filehandle. The loop typically ends when the file contains no more lines. It is not necessary to check for eof. Under some circumstances, you may need while(defined ($line = <$fh>)){} to prevent a warning message.
    • Your code is not clear with @ref. I passed a reference to that array that the subroutine modifies.
    • I opened a file handle to a memory variable. I did that to produce a single runable Perl program file.
    #!/usr/bin/perl use strict; use warnings; my $text=<<END; This is an example test file that has xyz line following the matching line 2nd line after the matching line END open my $fh, '<', \$text or die "unable to open text $!"; my @array; while (my $line =<$fh>) { SubOut($fh, \@array, $line) if $line =~ /xyz/; } close $fh; print @array; sub SubOut { my ($fh,$array_ref, $match_line) = @_; push @$array_ref, $match_line; while (my $s_line = <$fh>) { print "$s_line"; } } __END__ Prints: line following the matching line 2nd line after the matching line that has xyz <--- this is in @array
Re: Passing a filehandle to subroutine
by Laurent_R (Abbot) on Aug 10, 2017 at 22:16 UTC

    as suggested by hippo, your SubOut will read the whole file on its first call, and that's probably not what you want. But you don't say enough for me to really understand what you really want.

    As a couple of side notes, I would strongly suggest that you use lexical filehandles (rather than bare word filehandles, as this would make your life much easier if you want to pass the file handle to a subroutine:

    open my $F, "<", $file or die;
    Now, you can pass the filehandle as an argument to a subroutine just as a normal variable.

    I would also suggest that you use more idiomatic Perl, because idiomatic constructs are battle tested and avoid common traps. For example, to read a file

    open my $F, "<", $file or die; while (my $line = <$F>) { if ($line ...) { # ... } }
Re: Passing a filehandle to subroutine
by GotToBTru (Prior) on Aug 10, 2017 at 18:02 UTC

    What do you mean by losing the line? That it does not print or that it does not get stored in @ref? Consider the following suggestions:

    1. Use tell() to remember the position in the file before you read each line, and use seek() to reposition the pointer to there before calling SubOut
    2. Print $line before calling SubOut or pass $line to SubOut and push it onto @ref there

    But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1197186]
Front-paged by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2017-08-24 10:49 GMT
Find Nodes?
    Voting Booth?
    Who is your favorite scientist and why?

    Results (367 votes). Check out past polls.