Created
July 4, 2015 19:34
-
-
Save beppu/6f42d537f7d8cffbfd35 to your computer and use it in GitHub Desktop.
an example of how to control a subprocess and communicate with it through stdin/stdout/stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
use IPC::Open3; | |
use IO::Select; | |
use Symbol qw(gensym); | |
use POSIX qw(:sys_wait_h); | |
# Read as much as possible from a file handle and return the result. | |
# | |
# @param {IO::Handle} $fh a file handle | |
# @return {String} data read from the given filehandle | |
sub drain { | |
my $fh = shift; | |
my $result = ''; | |
my $buffer; | |
while (sysread($fh, $buffer, 8192)) { | |
$result .= $buffer; | |
} | |
$result; | |
} | |
# This supposedly happens if you try to write to $in after the child process exits. | |
$SIG{PIPE} = sub { | |
warn "PIPE"; | |
my $child_process_exit_code = $? >> 8; | |
exit $child_process_exit_code; | |
}; | |
# Start the child process we intend to control. | |
my ($child_process, @args) = @ARGV; | |
$child_process ||= "/usr/bin/dc"; | |
my ($in, $out, $err); | |
$err = gensym; # Only doing it, because the IPC::Open3 docs do this. | |
my $pid = open3($in, $out, $err, $child_process, @args); | |
# Set all the file handles to non-blocking so sysread won't block. | |
STDIN->blocking(0); | |
$in->blocking(0); | |
$out->blocking(0); | |
$err->blocking(0); | |
# Use IO::Select to determine which file handles need attention. | |
my $select = IO::Select->new; | |
$select->add(*STDIN); | |
$select->add($out); | |
$select->add($err); | |
COMMAND: while (my @ready = $select->can_read) { | |
for my $fh (@ready) { | |
no warnings; | |
if ($fh == $out) { | |
my $data = drain($out); | |
print "OUT: ", $data if $data; | |
} elsif ($fh == $err) { | |
my $data = drain($err); | |
print "ERR: ", $data if $data; | |
} elsif ($fh == *STDIN) { | |
# If you were using SDL, your input code would go here. | |
# Instead of reading STDIN, you'd be turning keypresses | |
# into chars to syswrite to $in. | |
my $data = drain(*STDIN); | |
print "IN: ", $data; | |
if (length $data) { | |
syswrite($in, $data); | |
} else { | |
close($in); | |
last COMMAND; | |
} | |
} else { | |
last COMMAND; | |
} | |
} | |
# If the child process has exited, quit the while loop. | |
last if waitpid($pid, WNOHANG); | |
} | |
# Make sure we wait until the child process has exited. | |
waitpid($pid, 0); | |
# exit using the child process' exit code. | |
exit $? >> 8; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment