++ed by:
RRWO VOJ YZHERNAND AYOUNG IWATA
7 non-PAUSE users
Author image Barrie Slaymaker

NAME

IPC::Run3 - Run a subprocess in batch mode (a la system) on Unix, Win32, etc.

SYNOPSIS

    use IPC::Run3;    ## Exports run3() by default
    use IPC::Run3 (); ## Don't pollute

    run3 \@cmd, \$in, \$out, \$err;
    run3 \@cmd, \@in, \&out, \$err;

DESCRIPTION

This module allows you to run a subprocess and redirect stdin, stdout, and/or stderr to files and perl data structures. It aims to satisfy 99% of the need for using system()/qx``/open3() with a simple, extremely Perlish API and none of the bloat and rarely used features of IPC::Run.

Speed (of Perl code; which is often much slower than the kind of buffered I/O that this module uses to spool input to and output from the child command), simplicity, and portability are paramount. Disk space is not.

Note that passing in \undef explicitly redirects the associated file descriptor for STDIN, STDOUT, or STDERR from or to the local equivalent of /dev/null (this does not pass a closed filehandle). Passing in "undef" (or not passing a redirection) allows the child to inherit the corresponding STDIN, STDOUT, or STDERR from the parent.

Because the redirects come last, this allows STDOUT and STDERR to default to the parent's by just not specifying them; a common use case.

Note: This means that:

    run3 \@cmd, undef, \$out;   ## Pass on parent's STDIN

does not close the child's STDIN, it passes on the parent's. Use

    run3 \@cmd, \undef, \$out;  ## Close child's STDIN

for that. It's not ideal, but it does work.

If the exact same value is passed for $stdout and $stderr, then the child will write both to the same filehandle. In general, this means that

    run3 \@cmd, \undef, "foo.txt", "foo.txt";
    run3 \@cmd, \undef, \$both, \$both;

will DWYM and pass a single file handle to the child for both STDOUT and STDERR, collecting all into $both.

DEBUGGING

To enable debugging use the IPCRUN3DEBUG environment variable to a non-zero integer value:

    $ IPCRUN3DEBUG=1 myapp

.

PROFILING

To enable profiling, set IPCRUN3PROFILE to a number to enable emitting profile information to STDERR (1 to get timestamps, 2 to get a summary report at the END of the program, 3 to get mini reports after each run) or to a filename to emit raw data to a file for later analysis.

COMPARISON

Here's how it stacks up to existing APIs:

system(), qx'', open "...|", open "|..."
+

redirects more that one file descriptor

+

returns TRUE on success, FALSE on failure

+

throws an error if problems occur in the parent process (or the pre-exec child)

+

allows a very perlish interface to perl data structures

+

allows 1 word invocations to avoid the shell easily.

-

leaves the result in $?

open2(), open3()
+

No need to risk a deadlock or avoid it with a length select() loop

+

Hides OS dependancies

+

Parameter order is like open3() (not like open2()).

+

Synchronizes with the child process so you get an exception if the child process fails to run.

IPC::Run::run()
+

Smaller, lower overhead, simpler, more portable

+

No select() loop

+

Does not fall prey to Perl closure leaks

-

Does not allow interaction with the subprocess (which IPC::Run::run() allows by redirecting subroutines).

-

Lacks many features of IPC::Run::run() (filters, pipes, redirects, pty support).

this is what a pretty well optimized version of the above looks like. It's about 56% lower overhead than the above, but that pales in comparison to the time spent in system().

Using perl -e1 as the test program, this version reduced the overall time from 9 to 7.6 seconds for 1000 iterations, about 1.4 seconds or 15%. The overhead time (time in run3()) went from 2.5 to 1.1 seconds, or 56%.

my $in_fh; my $in_fd; my $out_fh; my $out_fd; my $err_fh; my $err_fd; $in_fh = tempfile; $in_fd = fileno $in_fh; $out_fh = tempfile; $out_fd = fileno $out_fh; $err_fh = tempfile; $err_fd = fileno $err_fh; my $saved_fd0 = dup 0; my $saved_fd1 = dup 1; my $saved_fd2 = dup 2; my $r; my ( $cmd, $stdin, $stdout, $stderr );

sub _run3 { ( $cmd, $stdin, $stdout, $stderr ) = @_;

    truncate $in_fh, 0;
    seek $in_fh, 0, 0;

    print $in_fh $$stdin or die "$! writing to temp file";
    seek $in_fh, 0, 0;

    seek $out_fh, 0, 0;
    truncate $out_fh, 0;

    seek $err_fh, 0, 0;
    truncate $err_fh, 0;

    dup2 $in_fd,  0 or croak "run3(): $! redirecting STDIN";
    dup2 $out_fd, 1 or croak "run3(): $! redirecting STDOUT";
    dup2 $err_fd, 2 or croak "run3(): $! redirecting STDERR";

    $r = 
       system {$cmd->[0]}
               is_win32
                   ? map {
                       ## Probably need to offer a win32 escaping
                       ## option, every command is different.
                       ( my $s = $_ ) =~ s/"/"""/g;
                       $s;
                   } @$cmd
                   : @$cmd;

    die $! unless defined $r;

    dup2 $saved_fd0, 0;
    dup2 $saved_fd1, 1;
    dup2 $saved_fd2, 2;

    seek $out_fh, 0, 0 or croak "$! seeking on temp file for child output";

        my $count = read $out_fh, $$stdout, 10_000;
        while ( $count == 10_000 ) {
            $count = read $out_fh, $$stdout, 10_000, length $$stdout;
        }
        croak "$! reading child output from temp file"
            unless defined $count;

    seek $err_fh, 0, 0 or croak "$! seeking on temp file for child errput";

        my $count = read $err_fh, $$stderr, 10_000;
        while ( $count == 10_000 ) {
            $count = read $err_fh, $$stderr, 10_000, length $$stdout;
        }
        croak "$! reading child stderr from temp file"
            unless defined $count;

    return 1;
}

TODO

pty support

LIMITATIONS

Often uses intermediate files (determined by File::Temp, and thus by the File::Spec defaults and the TMPDIR env. variable) for speed, portability and simplicity.

COPYRIGHT

    Copyright 2003, R. Barrie Slaymaker, Jr., All Rights Reserved

LICENSE

You may use this module under the terms of the BSD, Artistic, or GPL licenses, any version.

AUTHOR

Barrie Slaymaker <barries@slaysys.com>