Skip to content

Instantly share code, notes, and snippets.

@shalk
Created October 15, 2013 09:21
Show Gist options
  • Save shalk/6988937 to your computer and use it in GitHub Desktop.
Save shalk/6988937 to your computer and use it in GitHub Desktop.
usage of IPC::Open3 with IO::Select http://www.perlmonks.org/?node_id=150748
###########################################################
#!/usr/bin/perl -w
# this script printf to stdout and stderr. It prints random
# characters and does not flush the output of stdout. stderr
# is autoflushed by default.
# uncomment the line about autoflush STDOUT to see how that
# changes the behavior. Also, you can uncomment the sleep
# line to watch the script in slow motion.
use warnings;
use strict;
use IO::Handle;
#autoflush STDOUT 1;
for (1..100){
my $str = '';
for(1..80){
$str .= ('A'..'Z','a'..'z',0..9)[rand 62];
}
if(int rand 2){ # 50:50 chance
print STDOUT "StdOut:$str";
} else {
print STDERR "StdErr:$str";
}
# sleep 1;
}
############################################################
#!/usr/bin/perl -w
# this script runs the previous script and catches both
# child's stdout and stderr. It prints progress information
# as it goes. It also prints the data it gets to the
# corresponding file.
use strict;
use warnings;
#use diagnostics;
use IPC::Open3;
use IO::Select;
use Symbol;
my $cmd = "./test.pl";
open(ERRLOG, ">error.log") or die "Can't open error log! $!";
open(OUTPUT, ">output.log") or die "Can't open output log! $!";
my ($infh,$outfh,$errfh);
$errfh = gensym(); # if you uncomment this line, $errfh will
# never be initialized for you and you
# will get a warning in the next print
# line.
my $pid;
eval{
$pid = open3($infh, $outfh, $errfh, $cmd);
};
die $@ if $@;
print "IN: $infh OUT: $outfh ERR: $errfh\n";
print "PID was $pid\n";
# now our child is running, happily printing to
# its stdout and stderr (our $outfh and $errfh).
my $sel = new IO::Select; # create a select object
$sel->add($outfh,$errfh); # and add the fhs
# $sel->can_read will block until there is data available
# on one or more fhs
while(my @ready = $sel->can_read) {
# now we have a list of all fhs that we can read from
foreach my $fh (@ready) { # loop through them
my $line;
# read up to 4096 bytes from this fh.
# if there is less than 4096 bytes, we'll only get
# those available bytes and won't block. If there
# is more than 4096 bytes, we'll only read 4096 and
# wait for the next iteration through the loop to
# read the rest.
my $len = sysread $fh, $line, 4096;
if(not defined $len){
# There was an error reading
die "Error from child: $!\n";
} elsif ($len == 0){
# Finished reading from this FH because we read
# 0 bytes. Remove this handle from $sel.
# we will exit the loop once we remove all file
# handles ($outfh and $errfh).
$sel->remove($fh);
next;
} else { # we read data alright
print "Read $len bytes from $fh\n";
if($fh == $outfh) {
print OUTPUT $line;
} elsif($fh == $errfh) {
print ERRLOG $line;
} else {
die "Shouldn't be here\n";
}
}
}
}
# now that the child closed both its handles, I assume it
# exited.
# ps will show you the <defunct> child.
print `ps`;
# go ahead and reap it
waitpid $pid, 0; # wait for it to die
# not it's no more
print `ps`;
close(ERRLOG) or die "Can't close filehandle! $!";
close(OUTPUT) or die "Can't close filehandle! $!";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment