[comp.lang.perl] open pipe in and out

usenet@carssdf.UUCP (John Watson) (07/11/90)

I have a perl script that needs to send a few lines to a filter
process and read a few lines back.  (I have no sockets, forget that.)
I would like to do something like  pipe, fork, exec, dup2, etc...
The way I do it in "c".  But the key here is that I need to be able
to use something like dup2 to make the pipe in and pipe out handles
look like STDIN and STDOUT to the child process before I do the exec.
I would like to retain the filter aspect, reads STDIN, writes STDOUT,
of the child process.  If that were not possible, I would still need
a way to pass the file handle numbers (the fd ints, not the *FILE's).

Any Ideas?     John Watson ...!rutgers!carssdf!usenet

poage@sunny.ucdavis.edu (Tom Poage) (07/11/90)

Here's some clips of code I've experimented with to perform multiple
(sorta ad-hoc) database queries from another program using a single
invocation of SQL.  The query language is written to the SQL process
and the results are read back from the same process.  I thought it'd be
efficient to start SQL only once since it is a BIG program with lots of
internal initialization.

BTW, I used a named pipe instead of piping stdin directly to SQL
because of the vendor's (Un*fy) stupid implementation; this SQL doesn't
know if it's getting input from a pipe or a tty, hence writes prompts,
etc., to stdout along with query results--this makes for ugly output.
The only way around it is to dupe SQL into thinking it's getting query
input from a file (Un*fy, do you read this group?).

Unfortunately, this example needs select(), so is not portable to all
UNIX domains.  In the end, I just used temporary files instead of pipes.

Lots deleted ....

$NAMED_PIPE = "/var/tmp/PIPE.$$";
if (system("/usr/etc/mknod $NAMED_PIPE p")) { die "mknod(named-pipe): $!\n"; }

...

# The pipe open fails (hangs) if the following construct is used.
# This happens on a Sun 3/150 (4.0.3)--anyone else?
#if (!open(SQLOUT, "SQL $NAMED_PIPE |")) { die "open(SQL): $!\n"; }
open(SQLOUT, "-|") || exec 'SQL', $NAMED_PIPE;
if (!open(SQLSCRIPT, ">$NAMED_PIPE")) { die "open(named-pipe): $!\n"; }

...

vec($rin, fileno(SQLOUT), 1) = 1;
vec($win, fileno(SQLSCRIPT), 1) = 1;

loop: for (;;) {
	$n = select($rout=$rin, $wout=$win, undef, undef);
	die "select error: $!\n" if ($n < 0);
	if ($wout eq $win) {
		# write some stuff to the process.
	}
	if ($rout eq $rin) {
		last loop unless ($_ = <SQLOUT>);
		# Do something with what you've just read.
	}
}

close(SQLOUT);
unlink($NAMED_PIPE);
exit(0);
-- 
Tom Poage, Clinical Engineering
Universiy of California, Davis, Medical Center, Sacramento, CA
poage@sunny.ucdavis.edu  {...,ucbvax,uunet}!ucdavis!sunny!poage

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (07/17/90)

In article <250@carssdf.UUCP> usenet@carssdf.UUCP (John Watson) writes:
: I have a perl script that needs to send a few lines to a filter
: process and read a few lines back.  (I have no sockets, forget that.)
: I would like to do something like  pipe, fork, exec, dup2, etc...
: The way I do it in "c".  But the key here is that I need to be able
: to use something like dup2 to make the pipe in and pipe out handles
: look like STDIN and STDOUT to the child process before I do the exec.
: I would like to retain the filter aspect, reads STDIN, writes STDOUT,
: of the child process.  If that were not possible, I would still need
: a way to pass the file handle numbers (the fd ints, not the *FILE's).

Here's a simple-minded routine.  It could be extended to use (or generate)
arbitrary filehandles, but for this one you just write to W1 and read
from R2.  Don't try to write more than 4096 bytes, of course, or you'll
deadlock...

#!/usr/bin/perl

sub filter {
    pipe(R1,W1);
    pipe(R2,W2);
    local($pid) = fork;
    if (!defined($pid)) {
	close R1;
	close R2;
	close W1;
	close W2;
    }
    elsif ($pid) {
	close R1;
	close W2;
    }
    else {
	close W1;
	close R2;
	open(STDIN,">&R1") || die "Can't dup to STDIN: $!\n";
	open(STDOUT,">&W2") || die "Can't dup to STDOUT: $!\n";
	close R1;
	close W2;
	print STDERR "execing @_\n";
	exec @_;
	die "Can't exec @_: $!\n";
    }
    $pid;
}

&filter('tr a-z A-Z');

while (<>) {
    print W1 $_;
}
close W1;

while (<R2>) {
    print;
}

Larry Wall
lwall@jpl-devvax.jpl.nasa.gov