in reply to Taming multiple external processes

Here's some code that will let you get the PID of each step in the pipeline. To avoid running a shell, pass each argument as a seperate parameter to pipeline.

pipeline takes as its first parameter the filehandle where the command's standard input should come from, and the rest of the parameters are the command to run. It returns a 2-element list, with the PID and output filehandle of the command. With your example, you'd do something like:

my($app1_pid,$app1_out)=pipeline(STDIN,'app1',$URL); my($app2_pid,$app2_out)=pipeline($app1_out,'app2'); while (<$app2_out>) { ... }

Here's the pipeline code, and a short example that runs sort |uniq -c |sort -rn. It's only tested slightly, so it may need some tweaking.

#!/usr/bin/perl use POSIX; my($pid1,$outfh1)=pipeline(STDIN,"sort"); my($pid2,$outfh2)=pipeline($outfh1,"uniq -c"); my($pid3,$outfh3)=pipeline($outfh2,"sort -rn"); warn "sort pid: $pid1\n"; warn "uniq pid: $pid2\n"; warn "sort2 pid: $pid3\n"; while (<$outfh3>) { print; } sub pipeline { my($in,@cmd)=@_; my($out,$child_write); pipe($out,$child_write) or die "pipe error: $!\n"; my $pid = fork; if (!defined($pid)) { die "fork error: $!\n" }; if ($pid) { # Parent close($child_write); return($pid,$out); } else { # child close($out); if (fileno($in) != 0) { close(STDIN) or die "Couldn't close STDIN: $!\n"; POSIX::dup2(fileno($in),0) or die "Couldn't dup to stdin: $!\n"; } close(STDOUT); POSIX::dup2(fileno($child_write),1) or die "Couldn't dup to stdout: $!\n"; exec(@cmd) or die "exec error: $!\n"; } }

Replies are listed 'Best First'.
We have a winner!
by 87C751 (Acolyte) on Jan 02, 2004 at 21:06 UTC
    sgifford, that is perfect! This code works regardless of the input to the first app (which resisted killing when it was reading from a net stream). Ignoring $SIG{CHLD} took care of zombies. (strangely, the auto-reap code from perlipc would kill my server process as well)

    This goes in the tool box, for sure. Thanks very much!