in reply to A way to report open file handles a Perl script has open?

Is there a way to report how many open file handles are open at once so I can know when I'm being wasteful?

Umm... yes, but for now it is - well, a hack.

This is a splendid use for inside-out objects, where information about an object is stored in the class which deals out the instances. That way you can ask the class about the state of any known object, if the class provides automatic garbage collection of objects that go out of scope. Perl includes a wonderful inside-out object class which does just this for any class: Hash::Util::FieldHash, based on Alter written by anno.

You would need to use modules from the IO family to initialize your file handles, consequently. That way your filehandles become objects and can be registered within the class.

The following is a quick hack, monkey-patching IO::File, but if it would be incorporated into IO::File, it would add 2 lines of code at the top level, 1 line of code in each of the methods new and open, and two class methods: open_fds and known_fds to query the class. In fact, known_fds would suffice, if it returns the objects which can then be queried further, but that can lead to more leakage if the variables holding the objects aren't freed.

This is for IO::File only; adapting this abomination to other modules of the IO family is left as an excercise for the reader ;-)

# file io-open-hack.pl use strict; use warnings; use IO::File; BEGIN{ package IO::File { use Hash::Util::FieldHash qw(id_2obj); Hash::Util::FieldHash::fieldhash my %fh; *IO::File::_new = \&IO::File::new; *IO::File::_open = \&IO::File::open; { no warnings 'redefine'; *IO::File::new = sub { my $io = &_new; $fh{$io} = { new => "@{[caller]}" }; $io; }; *IO::File::open = sub { my $io = $_[0]; $fh{$io}->{open} = "$_[1] at @{[caller]}"; &_open; }; } sub open_fds { grep { $_->{fh}->opened } known_fds(); } sub known_fds { map { { new => $fh{$_}->{new}, # next is dubious, it leaks the object... fh => id_2obj($_), # ... so we should return it stringified? #fh => "@{[id_2obj($_)]}", open => $fh{$_}->{open}, } } keys %fh; } } } package main; use IO::File; { my $fh = IO::File->new(); $fh->open( "blorfldyick",'>'); print $fh "foo\n"; print "after open:\n"; report_fds($_) for qw(open known); close $fh; print "after close:\n"; report_fds($_) for qw(open known); } # $fh is out of scope here print "after scope:\n"; report_fds($_) for qw(open known); sub report_fds { my $type = shift; my $meth = "${type}_fds"; my @fds = IO::File->$meth(); print " $type files:"; if (@fds) { print $/; print " fh: $_->{fh}\n" . " new: $_->{new}\n" . " open: $_->{open}\n" for @fds; } else { print " none\n"; } } __END__ after open: open files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 known files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 after close: open files: none known files: fh: IO::File=GLOB(0x137a3b8) new: main io-open-hack.pl 45 open: blorfldyick at main io-open-hack.pl 46 after scope: open files: none known files: none

This feature could be off by default and turned on inside IO::File via a debug import tag or some such.

perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'