I/O

fileno#

Return the OS-level file descriptor number behind a filehandle.

fileno looks past Perl’s buffered filehandle abstraction and gives you the raw integer descriptor the kernel uses. It is the bridge between Perl I/O and system calls that speak descriptors directly — select, POSIX::dup2, fcntl, or anything in IO::Poll / IO::Select. On a directory handle it asks the same question against dirfd(3), which not every platform provides.

Synopsis#

fileno FILEHANDLE
fileno DIRHANDLE
fileno EXPR            # EXPR evaluated as an indirect handle

What you get back#

  • An integer >= 0 — the OS file descriptor — for a normal open handle backed by a real kernel object.

  • -1 when the handle is open but has no real descriptor. This is what you get for in-memory handles created by open with a scalar reference as the third argument (open my $fh, ">", \$buf).

  • undef when the handle is closed, never opened, or — on a directory handle — when the platform lacks dirfd(3). In the dirhandle case $! is set.

The three return classes matter. Code that checks fileno($fh) for truth conflates “no such fd” with “fd number 0” (STDIN). Always compare explicitly:

my $fd = fileno $fh;
die "handle not open"      unless defined $fd;
die "handle has no real fd" if $fd == -1;

Global state it touches#

  • $! — set when fileno on a directory handle fails because the platform has no dirfd(3) equivalent.

fileno does not read or write any other I/O globals. It does not flush, it does not seek, it does not change the currently selected handle.

Examples#

The common case — turn a Perl filehandle into the fd number a system call wants:

open my $fh, "<", "/etc/hostname" or die $!;
print fileno $fh, "\n";             # e.g. "3"

Standard handles always have fixed descriptors 0, 1, 2:

print fileno STDIN,  "\n";          # 0
print fileno STDOUT, "\n";          # 1
print fileno STDERR, "\n";          # 2

Check whether two handles are duplicates of the same underlying descriptor (the idiom from the upstream POD, adjusted for the -1 case):

if (fileno($this) != -1 && fileno($this) == fileno($that)) {
    print "\$this and \$that are dups\n";
} elsif (fileno($this) != -1 && fileno($that) != -1) {
    print "\$this and \$that have different "
        . "underlying file descriptors\n";
} else {
    print "at least one handle has no real file descriptor\n";
}

In-memory handles have no kernel object, so fileno returns -1:

my $buf = "";
open my $mh, ">", \$buf or die $!;
print fileno $mh, "\n";             # -1

Build a descriptor bitmap for select — the textbook use for fileno:

my $rin = "";
vec($rin, fileno($sock),  1) = 1;
vec($rin, fileno(STDIN),  1) = 1;
my $n = select(my $rout = $rin, undef, undef, 5.0);

Directory handles, where the platform supports dirfd(3):

opendir my $dh, "." or die $!;
my $dfd = fileno $dh;
if (defined $dfd) {
    # use with fstat, openat, fchdir, ...
} else {
    warn "no dirfd on this platform: $!";
}

Edge cases#

  • Closed handle: returns undef. No warning under use warningsfileno is deliberately quiet so it can be used as a probe.

  • Never-opened bareword: fileno NOSUCH returns undef; strict mode will refuse the bareword at compile time unless it’s declared.

  • In-memory scalar handles: return -1, not undef. Treating -1 as “open but unusable for system calls” is the intended contract — the handle itself still works for print, readline, etc.

  • fileno(-1) trap: an explicit if (fileno $fh) { ... } is a bug waiting to happen: fd 0 is STDIN. Use defined and compare against -1 explicitly.

  • Indirect filehandle via expression: if FILEHANDLE is any expression other than a bareword or simple scalar, its value is taken as an indirect handle name (a string), not as a handle object. To pass a handle held in a data structure, dereference first:

    fileno $handles[0];                 # indirect: uses the string
    fileno ${ \$handles[0] };           # still wrong
    fileno $handles[0]->fileno;         # wrong: calling a method on ""
    my $fh = $handles[0]; fileno $fh;   # right
    
  • Directory handle on platforms without dirfd(3): returns undef and sets $!. Linux has dirfd(3), so on pperl’s supported targets this path is not exercised.

  • Tied handles: fileno calls the tie class’s FILENO method if defined; otherwise returns undef. The tied class is responsible for returning a sensible value (-1 for “no fd” is the convention).

  • Dup’d handles: two handles opened with open from the same underlying fd (e.g. open my $copy, ">&", $orig) return the new descriptor, not the original — the dup created a fresh fd.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • open — creates the filehandles that fileno inspects; the >& and <&= modes take descriptor numbers that round-trip through fileno

  • opendir — source of directory handles for the dirhandle form of fileno

  • close — after which fileno on the same handle returns undef

  • select — the four-argument form consumes bitmaps built from fileno values

  • binmode — works on the PerlIO layer above the fd; fileno reaches beneath it