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

Hi,

I' getting the following error when trying to print after opening a text file:

print() on closed filehandle WRITE_FH

Not sure why I'm getting this error the file is closed after being written to before opening again.

use strict; use HTML::TreeBuilder 3; open(WRITE_FH, '>', $filename) or die "cannot open $filename"; select WRITE_FH; # ... # ... everything you print should be redirected to your file # ... my $root = HTML::TreeBuilder->new; $root->parse_file('file2.html'); my @div_class = $root->find_by_attribute("class","forminput"); foreach my $node (@div_class) { print $node->content_list(); print "\n"; } close WRITE_FH; #prnt elements in array: open( my $fh, '<', $filename ) or die "Can't open $filename: $!"; my @line = <$fh>; print "\n"; print "Computer Name: $line[0]"; print "IP Address: $line[1]"; print "Root Server: $line[2]"; print "OS: $line[3]"; print "Last Report Time: $line[4]"; print "\n"; close $fh;

Thanks

Replies are listed 'Best First'.
Re: print() on closed filehandle WRITE_FH
by haukex (Archbishop) on Sep 25, 2018 at 18:21 UTC

    select changes the default output for print statements, and you've got some print statements after you close WRITE_FH that are then still trying to write to the file. You need to save the original output handle by first saying my $orig_fh = select(WRITE_FH); and then restore it later by saying select($orig_fh); right before or after you close the file.

      Hi haukex,

      I'm till getting the same error. Probably because I've added your suggestions in the wrong place.

      use strict; use HTML::TreeBuilder 3; open(WRITE_FH, '>', $filename) or die "cannot open $filename"; select WRITE_FH; # ... # ... everything you print should be redirected to your file # ... my $root = HTML::TreeBuilder->new; $root->parse_file('file2.html'); my @div_class = $root->find_by_attribute("class","forminput"); foreach my $node (@div_class) { # print $node->content_list(); # print "\n"; } my $orig_fh = select(WRITE_FH); select($orig_fh); close WRITE_FH; #prnt elements in array: open( my $fh, '<', $filename ) or die "Can't open $filename: $!"; my @line = <$fh>; print "\n"; print "FillDB File Size Limit: $line[0]"; print "FillDB File Count Limit: $line[1]"; print "Timeout for queries in queue: $line[2]"; print "Size of queries in queue: $line[3]"; print "Size of results queue: $line[4]"; print "\n"; close $fh;
        Probably because I've added your suggestions in the wrong place.

        Yes, and pryrt already explained the issue. But then you posted here, so it seems like you didn't get it working?

        If you take a look at the documentation I linked to, and the replies you've gotten, you'll see that:

        • print "..."; prints to the currently selected filehandle, which is STDOUT by default
        • print $filehandle "..."; and print {$filehandle} "..."; print to that specific filehandle
        • select both changes the currently selected filehandle, and returns the previously selected filehandle

        If you go through your program line-by-line, the above information should be enough for you to see what is going wrong and how to fix it. If you have questions about the above, please always feel free to ask.

        I understand the appeal of being able to copy and paste code and just have it work. However, I find it so much more satisfying to figure it out myself, to understand the flow of the code and what is going on. And of course, having it figured out means I'll know more about what I'm doing next time. In fact, I intentionally didn't post a block of code in an attempt to encourage you to look at the docs and figure it out :-)

        You have to do the my $orig_fh = select(WRITE_FH); the first time you select WRITE_FH -- otherwise, you've already lost the original file handle. Then, just before or just after the close WRITE_FH, you need to select the original again using select($orig_fh), so that it's active after you've closed WRITE_FH.
Re: print() on closed filehandle WRITE_FH
by Perlbotics (Archbishop) on Sep 25, 2018 at 18:23 UTC

    Here

    close WRITE_FH;
    your program closes the file, but here
    select WRITE_FH;
    the output filehandle for print(...) was redirected to the now closed FH. Use  print $write_fh ... or print WRITE_FH ... or reset the output back to STDOUT using select *STDOUT; (or backup/restore the previous filehandle from the initial select() if it was not the standard output).

    Update: I would suggest not to use select unless you might have to redirect output of some other modules (which are not under your control or you wouldn't want to mess with). Instead, please use - as you've already done in the second part - the three argument open with a lexically scoped (my $write_fh ...) variable.
    Disadvantage: more typing (print $write_fh ...).
    Advantages: Intent is made clear. Mixes well with regular output. Automatic close when $write_fh goes out of scope. No interference with other modules that might use select, etc.

Re: print() on closed filehandle WRITE_FH
by afoken (Chancellor) on Sep 25, 2018 at 22:27 UTC

    In addition to the other helpful postings:

    I would really avoid changing the default handle to something else than STDOUT. In other words: Don't do select HANDLE followed by print $text, printf $format,..., say $text. Instead, pass the handle explicitly to the writing function, i.e. print $handle $text, printf $handle $format,..., say $handle $text. (Note: no comma between handle and function arguments.)

    The reasons for that are simple:

    • Much existing code expects that those functions write to STDOUT
    • No spooky action-at-a-distance after select $handle
    • File I/O looks clearly different to writing to STDIO.
    • You can't forget to switch back to the default handle (select $oldhandle)

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      Hi afoken,

      I'm sorry but I am new to perl and not sure how to apply your suggestion.

      Would you happen to have an example handy that you could share?

      Thanks

        Download and save this as demo.pl:

        #!/usr/bin/perl use strict; use warnings; my $fn='lotr.txt'; open my $fh,'>',$fn or die "Could not open $fn: $!"; print "Three Rings for the Elven-kings under the sky,\n"; print $fh "The Road goes ever on and on\n"; print "Seven for the Dwarf-lords in their halls of stone,\n"; print $fh "Down from the door where it began.\n"; print "Nine for Mortal Men doomed to die,\n"; print $fh "Now far ahead the Road has gone,\n"; print "One for the Dark Lord on his dark throne\n"; print $fh "And I must follow, if I can,\n"; print "In the Land of Mordor where the Shadows lie.\n"; print $fh "Pursuing it with eager feet,\n"; print "One Ring to rule them all, One Ring to find them,\n"; print $fh "Until it joins some larger way\n"; print "One Ring to bring them all and in the darkness bind them\n"; print $fh "Where many paths and errands meet.\n"; print "In the Land of Mordor where the Shadows lie.\n"; print $fh "And whither then? I cannot say.\n"; close $fh;

        Then run it:

        X:\>perl demo.pl Three Rings for the Elven-kings under the sky, Seven for the Dwarf-lords in their halls of stone, Nine for Mortal Men doomed to die, One for the Dark Lord on his dark throne In the Land of Mordor where the Shadows lie. One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them In the Land of Mordor where the Shadows lie. X:\>type lotr.txt The Road goes ever on and on Down from the door where it began. Now far ahead the Road has gone, And I must follow, if I can, Pursuing it with eager feet, Until it joins some larger way Where many paths and errands meet. And whither then? I cannot say. X:\>
        /tmp>perl demo.pl Three Rings for the Elven-kings under the sky, Seven for the Dwarf-lords in their halls of stone, Nine for Mortal Men doomed to die, One for the Dark Lord on his dark throne In the Land of Mordor where the Shadows lie. One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them In the Land of Mordor where the Shadows lie. /tmp>cat lotr.txt The Road goes ever on and on Down from the door where it began. Now far ahead the Road has gone, And I must follow, if I can, Pursuing it with eager feet, Until it joins some larger way Where many paths and errands meet. And whither then? I cannot say. /tmp>

        (Both quotes from The Fellowship of the Ring by J. R. R. Tolkien)

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)