Murcia has asked for the wisdom of the Perl Monks concerning the following question:

Hi, i wanted to use ipc::open3 to control a process, here is the code of wrapper.pl
use strict; use IPC::Open3; use IO::Select; use Carp; my $cmd = "ls -al"; my $pid = open3( *cmdIn, *cmdOut, *cmdErr, $cmd) or die "can not open +$cmd $!"; print "\$pid $pid"; print "\n"; $SIG{CHLD} = sub { print "result: Status $? in $pid\n" if waitpid($pid, 0) >0 }; my $select = IO::Select->new(*cmdErr, *cmdOut); print cmdIn "\n\n$cmd\n"; close(cmdIn); my $input; while ( my @ready = $select->can_read()) { foreach my $fh ( @ready ) { if (fileno($fh) == fileno(cmdErr)) { process_errors(); } else { print "STDOUT: ", <$fh>; } $select->remove($fh) if eof($fh); } } # close (cmdOut); close(cmdErr); sub process_errors { my $select= IO::Select->new( *cmdErr ); my $input; while ( $select->can_read(0.1) ) { sysread *cmdErr, (my $line), 1024; last unless $line; $input.= $line; } croak "THERE: $input $cmd code" if $input =~ /error:/; carp "HERE: $input $cmd code" if $input; } { local $SIG {__WARN__} = sub { my $warning = shift ; # warning handler }; # commands }
When i run the wrapper.pl, often it does $cmd (ls -al), listing the files in the shell, but sometimes not, no output! WHY?
/home/murcia # ./wrapper.pl $pid 11685 <<<<< OK STDOUT: total 48 -rwxr-xr-x 1 murcia users 77 Dec 14 11:30 test.pl result: Status 0 in 11685 /home/murcia # ./wrapper.pl $pid 11687 <<<< not OK /home/murcia #
When I use a cmd which needs a short time, it seems to fail more often??? for a long cmd not??? Best regards Murcia

Replies are listed 'Best First'.
Re: ipc::open3 start process
by Corion (Patriarch) on Dec 14, 2004 at 11:26 UTC

    I think you are reading too soon from your child process and the socket isn't readable resp. doesn't have output on it yet:

    while ( my @ready = $select->can_read()) { ... };

    A quick fix could be to sleep some time before first trying to read output of the child:

    sleep 2; # give time to child process while ( my @ready = $select->can_read()) { ... };

    A better fix would most likely be to split up output reading and child checking into two parts, but checking if a child is still running/alive is hard to do unless you know on what OS you will be running:

    my @children = get_child_pids(); # fake while (@children) { while ( my @ready = $select->can_read()) { ... }; @children = get_child_pids(); # fake sleep 5; # let's see if the remaining children will produce output };

    I'm punting on the check for alive children. The easiest way to check for alive children would be if you're under Linux to check for -d /proc/$child_pid, and maybe there is some way in Perl to get at the list of PIDs associated with your current PID, but I don't know it at the moment. Somebody else will chime in with that, I hope. CPAN might know too.

      ... and maybe there is some way in Perl to get at the list of PIDs associated with your current PID ...

      You can look in the source of Proc::Killfam (here) to see how it's done there. It's basically a matter of walking the entire process table and checked the parent pid (ppid) of every process to see if it's your pid.

      --
      edan

Re: ipc::open3 start process
by amw1 (Friar) on Dec 14, 2004 at 16:23 UTC
    Here's the slug of code that I used when I was doing this recently. (figured I'd post the code rather than vauge this may work kinda stuff)

    my $out; my $err; my $pid = open3(*INFH, *OUTFH, *ERRFH, $cmd); my $sel = new IO::Select; $sel->add(*OUTFH, *ERRFH); while(my @ready_to_read = $sel->can_read) { foreach my $fh (@ready_to_read) { my $line; my $len = read($fh, $line, 4096); if(!defined($len)) { die "Whoops: $!"; } elsif($len != 0) { if(fileno($fh) == fileno(ERRFH)) { $err .= $line; } elsif(fileno($fh) == fileno(OUTFH)) { $out .= $line; } } elsif($len == 0) { $sel->remove($fh); } } } close(OUTFH); close(ERRFH); close(INFH); waitpid($pid, 0); # $err and $out are now ready for doing whatever you want # the return code from the command is available via '0xffff & $?'
    This has worked reliably for me. I haven't needed to write anything to the process so there may be issues there.
Re: ipc::open3 start process
by amw1 (Friar) on Dec 14, 2004 at 15:59 UTC
    I'm not entirely sure but there may be some wierdness with the mix of buffered IO and the sysread. You may try replacing your sysread call with a plain read call. I'd also do the same thing on the print STDOUT <$fh>, read from it and print until the handle is drained.

    I just worked on a similar chunk of code and switching to read everywhere seemed to clear up any issues I had with it. Didn't take the time to figure out exactly why (ran across a note somewhere about issues with mixing buffered IO and sysread)

Re: ipc::open3 start process
by zentara (Cardinal) on Dec 14, 2004 at 16:00 UTC
    I think your problem is the needed delay, as Corion pointed out.

    However, I was just looking this over offline and noticed that you have another "redundant" IO::Select->new() statement in your process_errors sub. I'm not sure if it affects your performance, but it isn't needed. You already have *cmdErr added to your select in main, so all you need to do is the sysread in your sub. It may "gum up the works" in IO::Select. ?


    I'm not really a human, but I play one on earth. flash japh