Recipe 7.10 Copying Filehandles

7.10.1 Problem

You want a copy of a filehandle.

7.10.2 Solution

To create an alias for a named filehandle, say:


Use open with the & file access mode to create an independent copy of the file descriptor for that filehandle:

open(OUTCOPY, ">&STDOUT")        or die "Couldn't dup STDOUT: $!";
open(INCOPY,  "<&STDIN")         or die "Couldn't dup STDIN : $!";

Use open with the &= mode to create an alias for that filehandle or file descriptor:

open(OUTALIAS, ">&=STDOUT")      or die "Couldn't alias STDOUT: $!";
open(INALIAS,  "<&=STDIN")       or die "Couldn't alias STDIN : $!";
open(BYNUMBER, ">&=5")           or die "Couldn't alias file descriptor 5: $!";

With other types of filehandles (typeglobs, objects), use the same technique with a three-argument open:

open(my $copy, "<&",  $original) or die "Couldn't alias original: $!";
open(my $copy, "<&=", $original) or die "Couldn't alias original: $!";

7.10.3 Discussion

If you create an alias for a filehandle with typeglobs, only one Perl I/O object is still being accessed. If you close one of these aliased filehandles, the I/O object is closed. Any further attempt to use a copy of that filehandle fails, silently by default or, if you have warnings enabled, with the warning "print on closed filehandle". When alternating access through aliased filehandles, writes work as you'd expect because there are no duplicated stdio data structures to get out of sync.

If you create a copy of a file descriptor with open(COPY, ">&HANDLE"), you're really calling the dup(2) syscall. You get two independent file descriptors whose file position, locks, and flags are shared, but which have independent stdio buffers. Closing one filehandle doesn't affect its copy. Simultaneously accessing the file through both filehandles is a recipe for disaster. Instead, this technique is normally used to save and restore STDOUT and STDERR:

# take copies of the file descriptors
open(OLDOUT, ">&STDOUT");
open(OLDERR, ">&STDERR");

# redirect stdout and stderr
open(STDOUT, "> /tmp/program.out")  or die "Can't redirect stdout: $!";
open(STDERR, ">&STDOUT")            or die "Can't dup stdout: $!";

# run the program

# close the redirected filehandles
close(STDOUT)                       or die "Can't close STDOUT: $!";
close(STDERR)                       or die "Can't close STDERR: $!";

# restore stdout and stderr
open(STDERR, ">&OLDERR")            or die "Can't restore stderr: $!";
open(STDOUT, ">&OLDOUT")            or die "Can't restore stdout: $!";

# avoid leaks by closing the independent copies
close(OLDOUT)                       or die "Can't close OLDOUT: $!";
close(OLDERR)                       or die "Can't close OLDERR: $!";

If you create an alias for a file descriptor using open(ALIAS, ">&=HANDLE"), you're really calling the fdopen(3) function from the stdio library or its equivalent. You get a single file descriptor with two stdio buffers accessed through two filehandles. Closing one filehandle closes the file descriptor of any aliases, but not their filehandlesif you tried to print to a filehandle whose alias had been closed, Perl wouldn't give a "print on closed filehandle" warning, even though the print failed. In short, accessing the file through both filehandles is also a recipe for disaster. This is really used only to open a file descriptor by number. See Recipe 7.9 for more information on this.

7.10.4 See Also

The open function in perlfunc(1) and in Chapter 29 of Programming Perl; your system's dup(2) manpage