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

Hello fellow monks

Will the file handle in this sub be closed automatically or is it accidentally left open? To the best of my knowledge it will be closed due to going out of scope but I'd like to confirm this.

sub slurp_file { my $file = shift; open my $fh, '+<', $file or croak("E: Unable to open file '$file': $!"); local $/; return <$fh>; }

(I know about File::Slurp, this code is just an example.)

Replies are listed 'Best First'.
Re: Scoping question - will file handle be closed?
by toolic (Bishop) on Jul 14, 2015 at 19:33 UTC
    From open:
    The filehandle will be closed when its reference count reaches zero. If it is a lexically scoped variable declared with my, that usually means the end of the enclosing scope. However, this automatic close does not check for errors, so it is better to explicitly close filehandles, especially those used for writing
      However, this automatic close does not check for errors, so it is better to explicitly close filehandles, especially those used for writing

      Besides knowing it, I always wondered what you're meant to do if you detect an error when closing a file?


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
      I'm with torvalds on this Agile (and TDD) debunked I told'em LLVM was the way to go. But did they listen!
        You're right, I also don't know. Most of the time, I do explicitly close my file handles, even if it is not necessary (at least, it is self-documenting), but I don't ever check whether closing the file was successful. Quite often, I close a bunch of files in one line such as:
        close $_ for qw/$IN1 $IN2 $OUT1 $OUT2/;
        (Except that this is just an example, I am usually trying to give more useful names to my file handles.)
        Warn the user that there's something wrong. The most likely cause is Perl failed to write the data to disk.
      Filehandles are global, irrespective of the scope in which they are referenced. To say the filehandle closes when the reference count reaches zero is incorrect. Try it...
      use strict; use warnings; if (1) { my $fh = \*FH; open $fh, 'ls /usr |'; } $_ = <FH>; warn $_;
      After the closing braces, there is nothing left in scope. In spite of that the filehandle FH remains open and $_ will be populated with the first file in /usr.

      Update: I didn't realise I was arguing against perldoc. LOL, oh well, I can't retract what I know to be true!

      One world, one people

        Filehandles are global, irrespective of the scope in which they are referenced. To say the filehandle closes when the reference count reaches zero is incorrect.

        oh well, I can't retract what I know to be true!

        No, because that would involve learning something?

        use strict; use warnings; if (1) { local *FH; my $fh = \*FH; open $fh, 'ls /usr |'; } $_ = <FH>; warn $_; __END__ ls: write error readline() on unopened filehandle FH at - line 10. Use of uninitialized value $_ in warn at - line 12. Warning: something's wrong at - line 12.

        Or, simpler demonstration:

        use strict; use warnings; { open my $fh, 'ls /usr |'; } warn "End\n"; __END__ ls: write error End

        Note how bin/ls complains before the script exits because my $fh is closed when the scope is left, before warn is called.

        - tye        

        Filehandles are global, irrespective of the scope in which they are referenced. To say the filehandle closes when the reference count reaches zero is incorrect.

        Not quite right, as you know by now, but also not completely wrong. Filehandles are a limited resource, no matter what language you use to write your software. So it is generally a good idea to close filehandles if they are no longer needed.

        Modern operating systems with a lot of memory generally allow many file handles, but still you can run out of filehandles:

        >perl -Mstrict -w -E 'my @h; for my $i (1..5000) { open $h[$i],"<","/d +ev/null" or die "$i $!"; }' 1022 Too many open files at -e line 1.

        (Linux 3.10.17 x64)

        C:\>perl -Mstrict -w -E "my @h; for my $i (1..5000) { open $h[$i],'<', +'nul' or die qq[$i $!]; }" 2046 Too many open files at -e line 1.

        (Windows 7 64 Bit)

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        FH contains the reference in this case. That's why you should use
        open my $FH, '-|', qw{ ls usr } or die $!;
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        After the closing braces, there is nothing left in scope. In spite of that the filehandle FH remains open

        ... which means that FH is indeed still in scope and that the reference count is therefore non-zero.

Re: Scoping question - will file handle be closed?
by MidLifeXis (Monsignor) on Jul 14, 2015 at 19:45 UTC

    Should be closed. Lexical variable falls out of scope, gets undefined, and resources assigned to it are reclaimed.

    As an example:

    package foo { sub DESTROY { warn "Destroying object" } sub new { warn "Creating object"; return bless {}, $_[0]; } } warn "Before block"; { my $obj = foo->new; } warn "After block";

    The same basic concept lives around any lexical variable, including lexical file handles.

    --MidLifeXis

Re: Scoping question - will file handle be closed?
by Laurent_R (Canon) on Jul 14, 2015 at 20:35 UTC
    Yes, it should be closed, because you are returning the lines read on $fh.

    But if you changed:

    local $/; return <$fh>;
    to:
    # local $/; no longer useful here return $fh;
    with the idea of reading from $fh in the caller, then it will clearly remain open.

      I'm aware that returning $fh instead of <$fh> would keep the file handle open. I just wanted to confirm that the normal scoping rules also apply to <$fh> and there is no evil magic trickery going on behind the scenes.

      What kind of evil? Hmm. Maybe <$fh> is actually evaluated lazily and does not return stuff unless someone actually reads from it keeping the $fh open. (Sorry, to much functional thinking recently...)

Re: Scoping question - will file handle be closed?
by locked_user sundialsvc4 (Abbot) on Jul 14, 2015 at 21:33 UTC

    Well, for what it’s worth, here’s the approach that I take ... in any language that I write programs in ... whenever possible.

    (1) I don’t pass file-handles around.   Instead, I bury them as private variables in objects that are responsible for doing, on behalf of the application, anything that has to be done with [that type of ...] file.   The very fact that there is a file involved, is buried as a concern of the object.   When the object is destroyed, its destructor closes the file handle explicitly.   Basically, the “reference count” of that file-handle never rises above one, and the handle is never cleaned-up by virtue of going out of scope.   I handle everything.

    (2) Every operating system uses buffering.   Even if you ask the operating system to write all the data to disk (e.g. Unix/Linux sync()), there is no positive guarantee that it will actually happen, and it is quite an expensive thing to ask an operating system to do if it actually does do what you asked.   There’s only one situation where I actually asked for this, and that was when I knew that the file-buffering system (SMARTDRV.EXE, which tells you how long ago it was) was buggy, and my program was intended to fix some of the consequences of that bugginess.

    (3) Programming languages ... Perl included ... use their own per-line buffering system to assemble lines of output.   You can rely upon them to flush those buffers before they close the file, but if you intend to write output that does not include the line-end sequence that they’re looking for, you might have to tell them to flush.