Rafael Garcia-Suarez wrote:

>Jos I. Boumans wrote:
>  
>
>>Basically, if we can either get:
>>a) pipes to work 'somehow' on win32 (that being ipc::run or a fix on 
>>ipc::open3 or teaching IPC::Cmd to use IPC::Run3).
>>    
>>
>
>I don't know whether we can hope to get a reliable version of this in
>the near future, so give me hints, windows hackers :) If we don't, then
>I'll remove IPC::Run and began integrating Archive::Tar and its
>dependencies (notably Compress::Zlib).
>
Both Nicholas and I have now asked exactly what the problem with 
IPC::Open3 on Win32 is, but have not yet received an answer.  Presumably 
nobody is quite sure what, if anything, the problem is, so I thought I'd 
try and find out.

The attached program contains what I believe is the relevant code, 
lifted from CPANPLUS::Tools::Cmd, to try open IPC::Open3 vs IPC::Run.

Running "perl test.pl run" runs "$^X -v" via IPC::Run and produces this 
output:

=====
C:\Temp>perl test.pl run
err=[0]
buffer=[
This is perl, v5.8.6 built for MSWin32-x86-perlio

Copyright 1987-2004, Larry Wall

Perl may be copied only under the terms of either the Artistic License 
or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using `man perl' or `perldoc perl'.  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

]
buferr=[]
bufout=[
This is perl, v5.8.6 built for MSWin32-x86-perlio

Copyright 1987-2004, Larry Wall

Perl may be copied only under the terms of either the Artistic License 
or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using `man perl' or `perldoc perl'.  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

]
=====

Running "perl test.pl open" runs the same $cmd via IPC::Open3 but 
produces just this:

=====
C:\Temp>perl test.pl open
err=[0]
buffer=[]
buferr=[]
bufout=[]
=====

And that's a $cmd that doesn't even involve pipes.

Changing the $cmd to qq[$^X -e "open FH, '>C:/Temp/testout'"] reveals 
that the $cmd definitely is being executed; we're just losing all its 
output.

But IPC::Open3 passes all its tests OK (and the tests include gathering 
output from the child), so perhaps the _open3_run() subroutine is doing 
something wrong or non-portable?

Adding some debug, I find that $sel->can_read() is returning an empty 
list.  If I change

    while (my @ready = $sel->can_read) {
        foreach my $fh (@ready) { # loop through buffered handles
            ...
        }
    }

to

    foreach my $fh ($outfh, $errfh) {
        ...
    }

then the output is now as expected.

Presumably IO::Select->can_read() doesn't work on Win32 because it uses 
a 4-arg select(), which is only implemented for sockets on Win32.  Is 
the above change safe, or did we need to call can_read() for some 
reason?  Would the proposed "selectable pipes" change have any impact on 
this?

What do I need to do to test out whether IPC::Open3 is working with 
pipes or not?  (Open3.t doesn't seem to include any such tests.)

- Steve


------------------------------------------------
This email has been scanned for viruses and content by the Radan Computational 
Webshield Appliances.
use strict;
use warnings;

use IPC::Open3;
use IPC::Run;
use IO::Select;
use Symbol;

MAIN: {
    my (@buffer,@buferr,@bufout);
    my $verbose = 0;

    ### STDOUT message handler
    my $_out_handler = sub {
        my $buf = shift;
        return unless defined $buf;

        print STDOUT $buf if $verbose;
        push @buffer, $buf;
        push @bufout, $buf;
    };

    ### STDERR message handler
    my $_err_handler = sub {
        my $buf = shift;
        return unless defined $buf;

        print STDERR $buf if $verbose;
        push @buffer, $buf;
        push @buferr, $buf;
    };

    my $cmd = "$^X -v";
    my @cmd = ref ($cmd) ? grep(length, @{$cmd}) : $cmd;

    printf "Running [%s]...\n", join(' ', @cmd) if $verbose;

    my $err;
    if (@ARGV and $ARGV[0] =~ /run/) {
        STDOUT->autoflush(1); STDERR->autoflush(1);

        @cmd = ref($cmd) ? ( [ @cmd ] )
                         : map { /[<>|&]/
                                    ? $_
                                    : [ split / +/ ]
                               } split( /\s*([<>|&])\s*/, $cmd );

        IPC::Run::run(@cmd, \*STDIN, $_out_handler, $_err_handler) or $err++;
    }
    elsif (@ARGV and $ARGV[0] =~ /open/) {
        my $rv;
        ($rv,$err) = _open3_run([EMAIL PROTECTED], $_out_handler, $_err_handler, $verbose);
    }
    else {
        die "Usage: $0 {run|open}\n";
    }

    $err ||= $?;

    print "err=[$err]\n";
    print "[EMAIL PROTECTED]";
    print "[EMAIL PROTECTED]";
    print "[EMAIL PROTECTED]";
}

sub _open3_run {
    my ($cmdref, $_out_handler, $_err_handler, $verbose) = @_;
    my @cmd = @$cmdref;

    my ($infh, $outfh, $errfh); # open3 handles

    my $pid = eval {
        IPC::Open3::open3(
            $infh   = Symbol::gensym(),
            $outfh  = Symbol::gensym(),
            $errfh  = Symbol::gensym(),
            @cmd,
        )
    };

    return (undef, $@) if $@;

    my $sel = IO::Select->new; # create a select object
    $sel->add($outfh, $errfh); # and add the fhs

    STDOUT->autoflush(1); STDERR->autoflush(1);
    $outfh->autoflush(1) if UNIVERSAL::can($outfh, 'autoflush');
    $errfh->autoflush(1) if UNIVERSAL::can($errfh, 'autoflush');

    while (my @ready = $sel->can_read) {
        foreach my $fh (@ready) { # loop through buffered handles
            # read up to 4096 bytes from this fh.
            my $len = sysread $fh, my($buf), 4096;

            if (not defined $len){
                # There was an error reading
                warn "Error from child: $!";
                return(undef, $!);
            }
            elsif ($len == 0){
                $sel->remove($fh); # finished reading
                next;
            }
            elsif ($fh == $outfh) {
                $_out_handler->($buf);
            } elsif ($fh == $errfh) {
                $_err_handler->($buf);
            } else {
                warn "IO::Select error";
                return(undef, $!);
            }
        }
    }

    waitpid $pid, 0; # wait for it to die
    return 1;
}

Reply via email to