in reply to Weirdness with IO::Select and IPC::Open3

I don't know why you're using select for this problem. If you were to use select, you'd have

use strict; use warnings; use IO::Handle qw( ); # For autoflush. use IO::Select qw( ); use IPC::Open3 qw( open3 ); use Symbol qw( gensym ); use constant BLK_SIZE => 64*1024; my $pid = open3( my $stdin_fh = gensym(), my $stdout_fh = gensym(), my $stderr_fh = gensym(), 'server.pl', ); $stdin_fh->autoflush(1); my $stdin_buf = ''; my $stdout_buf = ''; my $stderr_buf = ''; my $writers = IO::Select->new(); my $readers = IO::Select->new($stdout_fh, $stderr_fh); my %answers = ( "What is your name?" => 'ikegami', "What is your mission?" => 'To become the King of Halloween' +, "What is your favorite color?" => 'orange', ); while ($readers->count()) { my ($ready_readers, $ready_writers) = IO::Select::select($readers, +$writers, undef); foreach my $fh (@$ready_readers) { if ($fh == $stdout_fh) { my $rv = sysread($fh, $stdout_buf, BLK_SIZE, length($stdout_b +uf)); if (!defined($rv)) { $readers->remove($fh); # ...[ Do something in response to error from sysread. ].. +. } elsif (!$rv) { $readers->remove($fh); # ...[ Do something in response to EOF from sysread. ]... } else { while ($stdout_buf =~ s/(.*)\n//) { my $query = $1; if ($query =~ /Have a nice day!\z/) { $readers->remove($fh); print("$query\n"); } elsif ($answers{$query}) { $stdin_buf .= "$answers{$query}\n"; $writers->add($stdin_fh); } else { # ...[ Do something with unrecognised output from ST +DIN. ]... } } } } if ($fh == $stderr_fh) { my $rv = sysread($fh, $stderr_buf, BLK_SIZE, length($stderr_b +uf)); if (!defined($rv)) { $readers->remove($fh); # ...[ Do something in response to error from sysread. ].. +. } elsif (!$rv) { $readers->remove($fh); # ...[ Do something in response to EOF from sysread. ]... } else { # ...[ Do something in response to output to STDERR. ]... } } } foreach my $fh (@$ready_writers) { if (length($stdin_buf)) { # It's only safe to send one bytes unless you # somehow query the system to find otherwise. print($stdin_fh substr($stdin_buf, 0, 1, '')); # ...[ Do something if print returned an error. ]... } if (!length($stdin_buf)) { $writers->remove($fh); } } } waitpid($pid, 0); # ...[ Do something if waitpid returned an error. ]...

Replies are listed 'Best First'.
Re^2: Weirdness with IO::Select and IPC::Open3
by rastoboy (Monk) on Mar 21, 2011 at 04:35 UTC
    Hi ikegami, thanks for all the great responses. It's going to take me a while to study it all, but I was wondering--how would you do it without IO::Select? You seem to be suggesting it is less than ideal for the purpose, but all I know is when I google the errors I get when I don't use it, everyone says to use it :-).

    Color me confused.

      If you don't care about the child's STDERR, you can send it to the parent's STDERR (using '>STDERR' for open3's third arg). Then, all you need is

      ... while (<$stdout_fh>) { chomp; if (/Have a nice day!\z/) { print("$query\n"); } elsif ($answers{$query}) { print($stdin_fh "$answers{$query}\n"); } else { # ...[ Do something with unrecognised output from STDIN. ]... } }

      You can do this because you have a strict request-response protocol.

      If you had a more asynchronous protocol, you still have lots of simpler options than select. These include IPC::Run, Expect and threads.

      when I google the errors I get when I don't use it, everyone says to use it :-).

      What errors would that be?