Skip to content

Instantly share code, notes, and snippets.

@beppu
Created July 4, 2015 19:34
Show Gist options
  • Save beppu/6f42d537f7d8cffbfd35 to your computer and use it in GitHub Desktop.
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
#!/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