Opening and Closing File Descriptors by Number

Problem

You know which file descriptors you'd like to do I/O on, but Perl requires filehandles, not descriptor numbers.

Solution

To open the file descriptor, use the "<&=" or "<&" open modes or the IO::Handle module's fdopen class method:

open(FH, "<&=$FDNUM"); # open FH to the descriptor itself open(FH, "<&$FDNUM"); # open FH to a copy of the descriptor use IO::Handle; $fh->fdopen($FDNUM, "r"); # open file descriptor 3 for reading

To close one by number, either use the POSIX::close function or else first open it as we did previously.

Discussion

Occasionally you have a file descriptor but no filehandle. Perl's I/O system uses filehandles instead of file descriptors, so you have to make a new filehandle for an already open file descriptor. The "<&", ">&", and "+<&" modes to open will do this for reading, writing, and updating respectively. Adding an equal sign to these to make "<&=", ">&=", and "+<&=" is more parsimonious of file descriptors and nearly always what you want to do. That's because it used only a C-level fdopen function, not a dup2 system call.

If you have version 5.004 or better installed, you can use the IO::Handle object method. This is the same as:

use IO::Handle; $fh = IO::Handle->new(); $fh->fdopen(3, "r"); # open fd 3 for reading

Closing a file descriptor by number is even rarer. The POSIX::close function does so directly. If your system doesn't have a working POSIX library but does have a working syscall (and your sysadmin has installed the sys/syscall.ph file created with the h2ph translator), you can call the not very portable syscall function. It has peculiar parameters and return values: You have to add 0 to numbers and append the empty string ("") to strings to coerce them to the right types for C, and the error return value is -1 rather than false as in other Perl calls. Needless to say, use syscall only as a last resort.

Here's how you'd open the file descriptors that the MH mail system feeds its child processes. It identifies them in the environment variable MHCONTEXTFD:

$fd = $ENV{MHCONTEXTFD};
 open(MHCONTEXT, "<&=$fd") or die "couldn't fdopen $fd: $!"; # after processing close(MHCONTEXT) or die "couldn't close context file: $!";

If you want to close a descriptor by number, just open it first.

See Also

The open function in perlfunc (1) and in of Perl Developing; the documentation for the standard POSIX and IO::Handle modules (also in of Perl Developing); your system's fdopen (3) manpages