7

In the following small code, I do not get an error or a warning for lines [09] and [18]. The only warning I get is with line [21]:

use strict;                                              # [01]
use warnings FATAL => 'unopened';                        # [02]
                                                         # [03]
open(my $outHandleA, ">outputA.txt") or die ("A: $!\n"); # [04] Opened $outHandleA
print $outHandleA "FILE A\n";                            # [05]
close $outHandleA;                                       # [06] Closed $outHandleA
                                                         # [07]
print $outHandleA;                                       # [08]
print $outHandleA "ABC\n";                               # [09] <---
print $outHandleA;                                       # [10]
print "-----";                                           # [11]
                                                         # [12]
open(OUT, ">outputB.txt") or die ("B: $!\n");            # [13] Opened OUT
print OUT "FILE B\n";                                    # [14]
close OUT;                                               # [15] Closed OUT
                                                         # [16]
print OUT;                                               # [17]
print OUT "DEF\n";                                       # [18] <---
print OUT;                                               # [19]
                                                         # [20]
print DOES_NOT_EXIST_HANDLE "GHI\n";                     # [21] Raises a FATAL warning
                                                         # [22]
print "JKL";                                             # [23] Does not reach here (as expected)

However, shouldn't lines [09] and [18] also raise an error or a warning like the following since it is closed (unopened)?

  • ERROR: print() on closed filehandle $outHandleA at printingToClosedHandle.pl line 9.
  • WARNING: print() on unopened filehandle $outHandleA at printingToClosedHandle.pl line 9.

This might be an issue with my version of Perl which is "perl 5, version 28, subversion 1 (v5.28.1) built for MSWin32-x64-multi-thread". Furthermore, here is the output of the program I get below:

outputA.txt outputB.txt STDOUT
FILE A FILE B GLOB(0xfeb428)GLOB(0xfeb428)-----

The STDOUT output in above table is from lines [08], [10], and [11]. Please note that the value between the parenthesis (...) in the above table might change with each execution.

zdim
  • 64,580
  • 5
  • 52
  • 81
mak
  • 197
  • 9
  • You might be interested in the `autodie` pragma instead of trying to make warnings errors. – Shawn Feb 26 '21 at 11:05

2 Answers2

5

There is a difference between a filehandle that has been opened (initialized) and got closed, and one that has not been opened at all; attempting to print to them draws different warnings. One of 'unopened' entries in perldiag says

%s() on unopened %s
(W unopened) An I/O operation was attempted on a filehandle that was never initialized. You need to do an open(), a sysopen(), or a socket() call, or call a constructor from the FileHandle package.

while an entry for print() on closed filehandle says

print() on closed filehandle %s
(W closed) The filehandle you're printing on got itself closed sometime before now. Check your control flow.

They both get the warnings though.

When use warnings FATAL => 'unopened' alone is enabled then printing to a filehandle that had been initialized but then closed does not get a warning.

use warnings;
#use warnings FATAL => 'unopened';
use strict;
use feature 'say';

open my $stdout, '>&', *STDOUT;    # a copy

say $stdout "hi";
close $stdout;
say $stdout "to lexical, closed";  # it warns (l.10)

FATAL_warnings_unopened: { 
    no warnings;                   # change to FATAL for 'unopened'
    use warnings FATAL => 'unopened';

    say $stdout "with FATAL to closed fh";      # no warning
    close STDOUT;
    say "with FATAL to STDOUT";                 # no warning

    say NON_EXISTENT_FH "no such filehandle!";  # l.20
};

say STDERR 'done';

This prints

hi
say() on closed filehandle $stdout at warnings_FATAL.pl line 10.
say() on unopened filehandle NON_EXISTENT_FH at warnings_FATAL.pl line 20.

It does exit, per FATAL and an unopened filehandle NON_EXISTENT_FH inside the block, but there is no warnings for the print to $stdout right above it. There is a warning for the first such print. A print to STDOUT which just got closed doesn't get a warning either.

If we uncomment the use warnings FATAL... line in the very beginning (and remove other warnings lines) then no warnings are issued for printing to closed handles, at all.

A very close reading of the docs for the warnings pragma is helpful. In short, I'd suggest to always first have use warnings; on its own, and then add a statement for categories wanted to be made FATAL (where multiple categories may be added in the same statement).

zdim
  • 64,580
  • 5
  • 52
  • 81
  • I understand now. If I do not want to turn all the warnings but warn me of unopened and already closed filehandle, then I should just call: ```use warnings FATAL => 'unopened';``` & ```use warnings FATAL => 'closed';```. As a note, **(l.10)** and **l.20** in your comments reflect line numbers that print to the console. Furthermore, when you say **(and remove other warnings lines)**, I assume you are referring to ```with_FATAL_warnings: { ... };```. As for ```>&```, I should take a look at [Duping filehandles](https://perldoc.perl.org/functions/open#Duping-filehandles) in Perldoc. – mak Mar 01 '21 at 08:09
  • @MakotoWada Yes, to all points. So we need to have `use warnings;` (always), and then can add those wanted to be fatal as well (and you can add a few in one statement, see docs for the warnings pragma, for which I am adding link at the very bottom of this answer). As for "dup"-ing, some of the links in my other answer (on the question from which this one followed) are useful, I think. – zdim Mar 02 '21 at 06:23
  • Yes, regarding "dup"-ing, it is mentioned in [Perl's odd behavior when reassigning a filehandle variable from STDOUT to a file without undef()](https://stackoverflow.com/questions/66361138/perls-odd-behavior-when-reassigning-a-filehandle-variable-from-stdout-to-a-file#66363133) (the link which you refer to as "the links in my other answer (on the question from which this one followed))". Thanks. – mak Mar 03 '21 at 01:56
  • 1
    @MakotoWada Right, that is what I meant. This "dup"-ing (opening with `>&` mode) is a way to make a new, independent filehandle, which is a copy (to start with). People commonly say "dup" because it uses `dup2` syscall (see `man 2 dup`) which duplicates a filehandle. I think that a useful example is in [this post](https://stackoverflow.com/a/57156978/4653379) (linked in that answer), and [this post](https://stackoverflow.com/a/6417737/4653379) (from another link in that same answer) is particularly good, I think. – zdim Mar 03 '21 at 03:34
3

You've run afoul of a detail of use and the importing mechanism behind it. Namely, if you use Foo 'bar';, then only Foo::bar will be imported, even if Foo normally exports other things.

In the same way, use warnings FATAL => 'unopened'; does not turn all warnings. It turns on only 'unopened' warnings:

$ perl -E 'use warnings FATAL => "unopened" ; print $x;'
$ perl -E 'use warnings; use warnings FATAL => "unopened" ; print $x;'
Name "main::x" used only once: possible typo at -e line 1.
Use of uninitialized value $x in print at -e line 1.
Dave Sherohman
  • 45,363
  • 14
  • 64
  • 102