I am trying to determine if a given scalar holds a filehandle. It could have been passed to me from a bareword filehandle (i.e. \*FH
), a lexical filehandle, an IO::Handle, an IO::File, etc. So far, the only thing that seems to be consistent amongst the various flavors is that they all have a reftype
of "GLOB"
.

- 64,182
- 22
- 135
- 226
-
1possible duplicate of [How do I tell what type of value is in a Perl variable?](http://stackoverflow.com/questions/1731333/how-do-i-tell-what-type-of-value-is-in-a-perl-variable) – Ether Jul 09 '10 at 16:55
-
Oops sorry, that isn't an exact dupe. I wish I could retract that close vote! (But the link is still somewhat relevant.) – Ether Jul 09 '10 at 16:56
-
See [When does `ref($variable)` return 'IO'?](http://stackoverflow.com/questions/2955428/when-does-refvariable-return-io) for a similar question. – Zaid Jul 10 '10 at 10:27
-
Is the purpose just to validate input arguments, or to provide different behavior based on variable types (e.g. open a file if a string is passed in)? Best thing to do might be to just treat it like a filehandle and throw an exception if it doesn't behave like one. – runrig Jul 12 '10 at 16:16
-
If it is a normal scalar I want to try to open a file with that name in the scalar, if it is already a filehandle I want to use the filehandle. – Chas. Owens Jul 12 '10 at 18:16
-
If you want to tell the difference between a filehandle and a string, I'd probably just use the same method as in File::Copy. – runrig Jul 13 '10 at 20:26
-
@runrig yeah, but what File::Copy is doing is very bad. `UNIVERSAL::isa` breaks Classes that overload `isa`. – Chas. Owens Jul 13 '10 at 21:54
-
And using fileno() doesn't work for tied filehandles...there is no "good" solution. And read http://www.perlmonks.org/?node_id=615039 for one opinion on breaking isa in this situation. – runrig Jul 16 '10 at 20:51
6 Answers
Use the openhandle function from Scalar::Util:
openhandle FH
Returns FH if FH may be used as a filehandle and is open, or FH is a tied handle. Otherwise undef is returned.
$fh = openhandle(*STDIN); # \*STDIN $fh = openhandle(\*STDIN); # \*STDIN $fh = openhandle(*NOTOPEN); # undef $fh = openhandle("scalar"); # undef
The current implementation is similar to Greg Bacon's answer, but it has some additional tests.

- 61,471
- 9
- 126
- 175
Remember that you can do this:
$ perl -le '$fh = "STDOUT"; print $fh "Hi there"' Hi there
That's an ordinary string but still useful as a filehandle.
Looking at the source of IO::Handle
, its opened
is a thin wrapper around fileno
, which has a handy property:
Returns the file descriptor for a filehandle, or undefined if the filehandle is not open.
But there is one caveat:
Filehandles connected to memory objects via new features of open may return undefined even though they are open.
It appears then that a test along the lines of
$@ = "";
my $fd = eval { fileno $maybefh };
my $valid = !$@ && defined $fd;
will do what you want.
The code below checks representatives of
- in-memory objects
- named filehandles
- globs
- glob references
- glob names
- the standard input
FileHandle
instancesIO::File
instances- pipes
- FIFOs
- sockets
Run it yourself:
#! /usr/bin/perl
use warnings;
use strict;
use Fatal qw/ open /;
use FileHandle;
use IO::File;
use IO::Socket::INET;
my $SLEEP = 5;
my $FIFO = "/tmp/myfifo";
unlink $FIFO;
my $pid = fork;
die "$0: fork" unless defined $pid;
if ($pid == 0) {
system("mknod", $FIFO, "p") == 0 or die "$0: mknod failed";
open my $fh, ">", $FIFO;
sleep $SLEEP;
exit 0;
}
else {
sleep 1 while !-e $FIFO;
}
my @ignored = (\*FH1,\*FH2);
my @handles = (
[0, "1", 1],
[0, "hashref", {}],
[0, "arrayref", []],
[0, "globref", \*INC],
[1, "in-memory", do {{ my $buf; open my $fh, "<", \$buf; $fh }}],
[1, "FH1 glob", do {{ open FH1, "<", "/dev/null"; *FH1 }}],
[1, "FH2 globref", do {{ open FH2, "<", "/dev/null"; \*FH2 }}],
[1, "FH3 string", do {{ open FH3, "<", "/dev/null"; "FH3" }}],
[1, "STDIN glob", \*STDIN],
[1, "plain read", do {{ open my $fh, "<", "/dev/null"; $fh }}],
[1, "plain write", do {{ open my $fh, ">", "/dev/null"; $fh }}],
[1, "FH read", FileHandle->new("< /dev/null")],
[1, "FH write", FileHandle->new("> /dev/null")],
[1, "I::F read", IO::File->new("< /dev/null")],
[1, "I::F write", IO::File->new("> /dev/null")],
[1, "pipe read", do {{ open my $fh, "sleep $SLEEP |"; $fh }}],
[1, "pipe write", do {{ open my $fh, "| sleep $SLEEP"; $fh }}],
[1, "FIFO read", do {{ open my $fh, "<", $FIFO; $fh }}],
[1, "socket", IO::Socket::INET->new(PeerAddr => "localhost:80")],
);
sub valid {
local $@;
my $fd = eval { fileno $_[0] };
!$@ && defined $fd;
}
for (@handles) {
my($expect,$desc,$fh) = @$_;
print "$desc: ";
my $valid = valid $fh;
if (!$expect) {
print $valid ? "FAIL\n" : "PASS\n";
next;
}
if ($valid) {
close $fh;
$valid = valid $fh;
print $valid ? "FAIL\n" : "PASS\n";
}
else {
print "FAIL\n";
}
}
print "Waiting for sleeps to finish...\n";
All passes on an Ubuntu 9.10 box, so the caveat concerning in-memory objects does not seem to be a concern on that platform at least.
1: PASS hashref: PASS arrayref: PASS globref: PASS in-memory: PASS FH1 glob: PASS FH2 globref: PASS FH3 string: PASS STDIN glob: PASS plain read: PASS plain write: PASS FH read: PASS FH write: PASS I::F read: PASS I::F write: PASS pipe read: PASS pipe write: PASS FIFO read: PASS socket: PASS

- 134,834
- 32
- 188
- 245
-
It looks like `tell` is not portable and fails to detect valid things: "The return value of tell() for the standard streams like the STDIN depends on the operating system: it may return -1 or something else. tell() on pipes, fifos, and sockets usually returns -1." – Chas. Owens Jul 10 '10 at 10:17
-
Really cool. Makes me wonder why there isn't something like this on CPAN already. – Robert P Jul 13 '10 at 18:50
-
1@Robert P, there is. It's called [openhandle in Scalar::Util](http://search.cpan.org/perldoc?Scalar%3a%3aUtil#openhandle). See [my answer](http://stackoverflow.com/questions/3214647/what-is-the-best-way-to-determine-if-a-scalar-holds-a-filehandle/3807592#3807592). – cjm Sep 27 '10 at 20:31
-
It looks like `openhandle` fails the "FH3 string" test, but that could arguably be the correct response. – Robert P Mar 29 '11 at 21:02
But any scalar contains something that could be used as a filehandle. Strings can be filehandles: they are package handles, then.
We always used to use Symbol::qualify()
for this. I don’t know whether that’s still “the” way that’s commonly advocated, but it will work if you are passed bareword handles (which are just strings). It checks the caller
’s package, qualifying it appropriately.
here’s also Symbol::qualify_to_ref()
, which may perhaps be closer to what you’re looking for.
Here's how they both work. In the output below:
- The first item in the => list is what gets made by
qualify
- The second item in the => list is what gets made by
qualify_to_ref
- The third item in the => list is file
fileno
returns on the second item
The script that produces this is included below:
off to NotMain
string "stderr" => main::stderr, GLOB(0x811720), fileno 2
string *stderr => *NotMain::stderr, GLOB(0x879ec0), fileno undef
string *sneeze => *NotMain::sneeze, GLOB(0x811e90), fileno undef
string *STDERR => *main::STDERR, GLOB(0x835260), fileno 2
back to main
string *stderr => *main::stderr, GLOB(0x879ec0), fileno 2
string "STDOUT" => main::STDOUT, GLOB(0x8116c0), fileno 1
string *STDOUT => *main::STDOUT, GLOB(0x811e90), fileno 1
string *STDOUT{IO} => IO::File=IO(0x8116d0), GLOB(0x811e90), fileno 1
string \*STDOUT => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
string "sneezy" => main::sneezy, GLOB(0x879ec0), fileno undef
string "hard to type" => main::hard to type, GLOB(0x8039e0), fileno 3
string $new_fh => IO::Handle=GLOB(0x8046c0), IO::Handle=GLOB(0x8046c0), fileno undef
string "GLOBAL" => main::GLOBAL, GLOB(0x891ff0), fileno 3
string *GLOBAL => *main::GLOBAL, GLOB(0x835260), fileno 3
string $GLOBAL => main::/dev/null, GLOB(0x817320), fileno 3
string $null => GLOB(0x8907d0), GLOB(0x8907d0), fileno 4
off to NotMain
glob "stderr" => main::stderr, GLOB(0x811720), fileno 2
glob stderr => main::stderr, GLOB(0x811720), fileno 2
glob sneeze => main::sneeze, GLOB(0x81e490), fileno undef
glob *sneeze => GLOB(0x892b90), GLOB(0x892b90), fileno undef
glob *stderr => GLOB(0x892710), GLOB(0x892710), fileno undef
glob *STDERR => GLOB(0x811700), GLOB(0x811700), fileno 2
back to main
glob *stderr => GLOB(0x811720), GLOB(0x811720), fileno 2
glob STDOUT => main::STDOUT, GLOB(0x8116c0), fileno 1
glob "STDOUT" => main::STDOUT, GLOB(0x8116c0), fileno 1
glob *STDOUT => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
glob *STDOUT{IO} => IO::File=IO(0x8116d0), GLOB(0x811d50), fileno 1
glob \*STDOUT => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
glob sneezy => main::sneezy, GLOB(0x879ec0), fileno undef
glob "sneezy" => main::sneezy, GLOB(0x879ec0), fileno undef
glob "hard to type" => main::hard to type, GLOB(0x8039e0), fileno 3
glob $new_fh => IO::Handle=GLOB(0x8046c0), IO::Handle=GLOB(0x8046c0), fileno undef
glob GLOBAL => main::GLOBAL, GLOB(0x891ff0), fileno 3
glob $GLOBAL => main::/dev/null, GLOB(0x817320), fileno 3
glob *GLOBAL => GLOB(0x891ff0), GLOB(0x891ff0), fileno 3
glob $null => GLOB(0x8907d0), GLOB(0x8907d0), fileno 4
And here’s the script that generate that output:
eval 'exec perl $0 ${1+"$@"}'
if 0;
use 5.010_000;
use strict;
use autodie;
use warnings qw[ FATAL all ];
use Symbol;
use IO::Handle;
#define exec(arg)
BEGIN { exec("cpp $0 | $^X") } # nyah nyah nyah-NYAH nhah!!
#undef exec
#define CPP(FN, ARG) printf(" %6s %s => %s\n", main::short("FN"), q(ARG), FN(ARG))
#define QS(ARG) CPP(main::qual_string, ARG)
#define QG(ARG) CPP(main::qual_glob, ARG)
#define NL say ""
sub comma(@);
sub short($);
sub qual($);
sub qual_glob(*);
sub qual_string($);
$| = 1;
main();
exit();
sub main {
our $GLOBAL = "/dev/null";
open GLOBAL;
my $new_fh = new IO::Handle;
open(my $null, "/dev/null");
for my $str ($GLOBAL, "hard to type") {
no strict "refs";
*$str = *GLOBAL{IO};
}
fake_qs();
QS( *stderr );
QS( "STDOUT" );
QS( *STDOUT );
QS( *STDOUT{IO} );
QS( \*STDOUT );
QS( "sneezy" );
QS( "hard to type" );
QS( $new_fh );
QS( "GLOBAL" );
QS( *GLOBAL );
QS( $GLOBAL );
QS( $null );
NL;
fake_qg();
QG( *stderr );
QG( STDOUT );
QG( "STDOUT" );
QG( *STDOUT );
QG( *STDOUT{IO} );
QG( \*STDOUT );
QG( sneezy );
QG( "sneezy" );
QG( "hard to type" );
QG( $new_fh );
QG( GLOBAL );
QG( $GLOBAL );
QG( *GLOBAL );
QG( $null );
NL;
}
package main;
sub comma(@) { join(", " => @_) }
sub qual_string($) {
my $string = shift();
return qual($string);
}
sub qual_glob(*) {
my $handle = shift();
return qual($handle);
}
sub qual($) {
my $thingie = shift();
my $qname = qualify($thingie);
my $qref = qualify_to_ref($thingie);
my $fnum = do { no autodie; fileno($qref) };
$fnum = "undef" unless defined $fnum;
return comma($qname, $qref, "fileno $fnum");
}
sub short($) {
my $name = shift();
$name =~ s/.*_//;
return $name;
}
sub fake_qg { &NotMain::fake_qg }
sub fake_qs { &NotMain::fake_qs }
package NotMain; # this is just wicked
sub fake_qg {
say "off to NotMain";
QG( "stderr" );
QG( stderr );
QG( sneeze );
QG( *sneeze );
QG( *stderr );
QG( *STDERR );
say "back to main";
}
sub fake_qs {
say "off to NotMain";
package NotMain;
QS( "stderr" );
QS( *stderr );
QS( *sneeze );
QS( *STDERR );
say "back to main";
}
What can I say? Sometimes I really miss the C preprocessor.
I just know this one’s gonna get me talked about. ☺

- 78,834
- 30
- 123
- 180
-
Hey look, that stupid windbag otherwise known as `perlcritic` doesn’t even kvetch upon unary `open`. Shows what *they* know! – tchrist Nov 15 '10 at 14:53
io_from_any
from IO::Handle::Util
takes care of upgrading anything to something sane.

- 39,270
- 4
- 65
- 132
Here's an excerpt from File::Copy determining whether or not a variable is a file handle:
my $from_a_handle = (ref($from)
? (ref($from) eq 'GLOB'
|| UNIVERSAL::isa($from, 'GLOB')
|| UNIVERSAL::isa($from, 'IO::Handle'))
: (ref(\$from) eq 'GLOB'));

- 6,486
- 2
- 27
- 44
-
1Eww. `UNIVERSAL::isa` is a bad bad bad way of doing isa. I'd prefer `eval { $from->isa('GLOB') }` so that inheritance and overriding work correctly. So sayeth [the isa documentation.](http://search.cpan.org/~jesse/perl-5.12.1/lib/UNIVERSAL.pm). I mean, the example they give for what NOT to do is `$is_io = UNIVERSAL::isa($fd, "IO::Handle"); # BAD!` – Robert P Jul 09 '10 at 21:55
-
@Robert P: Actually for GLOB (and other basic reference types) the latest recommendation is to use Scalar::Util::reftype(). While I agree somewhat with all this, using the above code will give you the same results as File::Copy, a core module, and you'd have to go through some effort to make it not work. – runrig Jul 11 '10 at 21:53
-
@Robert P: And I experienced a discussion of this on PerlMonks (http://www.perlmonks.org/?node_id=615015) with no real conclusive answer for what is current best practice. – runrig Jul 13 '10 at 18:31
I tend to use:
eval { $fh->can('readline') }
Or can('print') in the case of handles I intend on writing to. This is mostly because I really only want to deal with filehandles in an OO-way anyway, so this accurately resolves whether the target can do what I expect of it. If you've already checked for the $fh being defined, you can probably leave off the eval.

- 11