use Carp;
use Symbol qw(gensym qualify);
-$VERSION = 1.0103;
+$VERSION = 1.0105;
@ISA = qw(Exporter);
@EXPORT = qw(open3);
in the parameter slot so it can be overwritten in the caller, or
an exception will be raised.
+The filehandles may also be integers, in which case they are understood
+as file descriptors.
+
open3() returns the process ID of the child process. It doesn't return on
failure: it just raises an exception matching C</^open3:/>. However,
C<exec> failures in the child are not detected. You'll have to
trap SIGPIPE yourself.
+Note if you specify C<-> as the command, in an analogous fashion to
+C<open(FOO, "-|")> the child process will just be the forked Perl
+process rather than an external command. This feature isn't yet
+supported on Win32 platforms.
+
+open3() does not wait for and reap the child process after it exits.
+Except for short programs where it's acceptable to let the operating system
+take care of this, you need to do this yourself. This is normally as
+simple as calling C<waitpid $pid, 0> when you're done with the process.
+Failing to do this can result in an accumulation of defunct or "zombie"
+processes. See L<perlfunc/waitpid> for more information.
+
If you try to read from the child's stdout writer and their stderr
writer, you'll have problems with blocking, which means you'll want
to use select() or the IO::Select, which means you'd best use
# fixed for 5.001 by Ulrich Kunitz <kunitz@mai-koeln.com>
# ported to Win32 by Ron Schmidt, Merrill Lynch almost ended my career
# fixed for autovivving FHs, tchrist again
+# allow fd numbers to be used, by Frank Tobin
+# allow '-' as command (c.f. open "-|"), by Adam Spiers <perl@adamspiers.org>
#
# $Id: open3.pl,v 1.1 1993/11/23 06:26:15 marc Exp $
#
close $_[0] or croak "$Me: close($_[0]) failed: $!";
}
+sub fh_is_fd {
+ return $_[0] =~ /\A=?(\d+)\z/;
+}
+
+sub xfileno {
+ return $1 if $_[0] =~ /\A=?(\d+)\z/; # deal with fh just being an fd
+ return fileno $_[0];
+}
+
my $do_spawn = $^O eq 'os2' || $^O eq 'MSWin32';
sub _open3 {
$dup_err = ($dad_err =~ s/^[<>]&//);
# force unqualified filehandles into caller's package
- $dad_wtr = qualify $dad_wtr, $package;
- $dad_rdr = qualify $dad_rdr, $package;
- $dad_err = qualify $dad_err, $package;
+ $dad_wtr = qualify $dad_wtr, $package unless fh_is_fd($dad_wtr);
+ $dad_rdr = qualify $dad_rdr, $package unless fh_is_fd($dad_rdr);
+ $dad_err = qualify $dad_err, $package unless fh_is_fd($dad_err);
my $kid_rdr = gensym;
my $kid_wtr = gensym;
# If she wants to dup the kid's stderr onto her stdout I need to
# save a copy of her stdout before I put something else there.
if ($dad_rdr ne $dad_err && $dup_err
- && fileno($dad_err) == fileno(STDOUT)) {
+ && xfileno($dad_err) == fileno(STDOUT)) {
my $tmp = gensym;
xopen($tmp, ">&$dad_err");
$dad_err = $tmp;
}
if ($dup_wtr) {
- xopen \*STDIN, "<&$dad_wtr" if fileno(STDIN) != fileno($dad_wtr);
+ xopen \*STDIN, "<&$dad_wtr" if fileno(STDIN) != xfileno($dad_wtr);
} else {
xclose $dad_wtr;
xopen \*STDIN, "<&=" . fileno $kid_rdr;
}
if ($dup_rdr) {
- xopen \*STDOUT, ">&$dad_rdr" if fileno(STDOUT) != fileno($dad_rdr);
+ xopen \*STDOUT, ">&$dad_rdr" if fileno(STDOUT) != xfileno($dad_rdr);
} else {
xclose $dad_rdr;
xopen \*STDOUT, ">&=" . fileno $kid_wtr;
# I have to use a fileno here because in this one case
# I'm doing a dup but the filehandle might be a reference
# (from the special case above).
- xopen \*STDERR, ">&" . fileno $dad_err
- if fileno(STDERR) != fileno($dad_err);
+ xopen \*STDERR, ">&" . xfileno($dad_err)
+ if fileno(STDERR) != xfileno($dad_err);
} else {
xclose $dad_err;
xopen \*STDERR, ">&=" . fileno $kid_err;
} else {
xopen \*STDERR, ">&STDOUT" if fileno(STDERR) != fileno(STDOUT);
}
+ if ($cmd[0] eq '-') {
+ croak "Arguments don't make sense when the command is '-'"
+ if @cmd > 1;
+ return 0;
+ }
local($")=(" ");
exec @cmd # XXX: wrong process to croak from
or croak "$Me: exec of @cmd failed";