[comp.lang.perl] pipe to sort and back

mark@DRD.Com (Mark Lawrence) (08/24/90)

I have some data I'm reading and before I write it on to the intended
recipient, I want to take a chunk of it and pipe it to sort and bring
sort's output back and then write that on.  Any way to easily set up a
bi-directional file handle (or pair of filehandles) to accomplish that?
-- 
mark@DRD.Com uunet!apctrc!drd!mark$B!J%^!<%/!!!&%m!<%l%s%9!K(B

henkp@ruuinf.cs.ruu.nl (Henk P. Penning) (08/26/90)

In article <1990Aug24.145831.13299@DRD.Com> mark@DRD.Com (Mark Lawrence) writes:

>I have some data I'm reading and before I write it on to the intended
>recipient, I want to take a chunk of it and pipe it to sort and bring
>sort's output back and then write that on.  Any way to easily set up a
>bi-directional file handle (or pair of filehandles) to accomplish that?

# Here is my solution.
# Subroutine open2(READHANDLE,WRITEHANDLE,arg,arg,...) expects
# names of a READHANDLE and a WRITEHANDLE (as strings).
# It sets up pipes, forks, and calls exec(arg,arg,...) in the child.
# 
# The example should print the first few lines of the sorted passwd file.
# 
# 				===  HenkP  ===
# 
# Henk P. Penning, Dept of Computer Science, Utrecht University.
# Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands.
# Telephone: +31-30-534106
# e-mail   : henkp@cs.ruu.nl (uucp to hp4nl!ruuinf!henkp)
# ---------------------------------------------------
sub open2 # ( READHANDLE, WRITEHANDLE, argument(s) to exec() )
  { local($p1,$p2,@execParms) = @_ ;
    eval "pipe($p1,APPOUT)" || die "open2 can't create pipe $p1/APPOUT" ; 
    eval "pipe(APPIN,$p2)"  || die "open2 can't create pipe APPIN/$p2" ; 

    $open2Pid = fork ;

    if ( $open2Pid == 0 ) # CHILD
      { close($p1)              || die "child can't close $p1" ;
        close($p2)              || die "child can't close $p2" ;
        close(STDIN)            || die "child can't close STDIN" ;
        close(STDOUT)           || die "child can't close STDOUT" ;
        open(STDIN, "<&APPIN")  || die "child can't redirect STDIN" ;
        open(STDOUT,">&APPOUT") || die "child can't redirect STDOUT" ;
        close(APPIN)            || die "child can't close APPIN" ;
        close(APPOUT)           || die "child can't close APPOUT" ;
        exec(@execParms) ;
        die "open2 can't exec " . join(' ',@execParms) ;
      }
    else # PARENT
      { close(APPIN)  || die "open2 can't close APPIN" ;
        close(APPOUT) || die "open2 can't close APPOUT" ;
      }
  }

sub flush # ( FILEHANDLE )
  { $oldselect = select($_[0]) ;
    $oldpipe = $| ; $| = 1 ; print "" ; $| = $oldpipe ;
    select($oldselect) ;
  }

$INP = 'INP' ; $OUT = 'OUT' ;

&open2($INP,$OUT,'sort | head') ;

open(PW,'/etc/passwd') || die "can't open /etc/passwd\n" ;
while ($_ = <PW> ) { print OUT "out> $_" ; }
close(OUT) || die "can't close OUT\n" ;

while ( $_ = <INP> )
  { print "<in $_" ; }

close(INP) || die "can't close INP\n" ;
-- 
Henk P. Penning, Dept of Computer Science, Utrecht University.
Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands.
Telephone: +31-30-534106
e-mail   : henkp@cs.ruu.nl (uucp to hp4nl!ruuinf!henkp)

henseler@uniol.UUCP (Herwig Henseler) (08/28/90)

Hello Perlers,

henkp@ruuinf.cs.ruu.nl (Henk P. Penning) writes:
>In article <1990Aug24.145831.13299@DRD.Com> mark@DRD.Com (Mark Lawrence) writes:
>>I have some data I'm reading and before I write it on to the intended
>>recipient, I want to take a chunk of it and pipe it to sort and bring
>>sort's output back and then write that on.  Any way to easily set up a
>>bi-directional file handle (or pair of filehandles) to accomplish that?

># Here is my solution.
>sub open2 # ( READHANDLE, WRITEHANDLE, argument(s) to exec() )
[...]

>&open2($INP,$OUT,'sort | head') ;
>open(PW,'/etc/passwd') || die "can't open /etc/passwd\n" ;
>while ($_ = <PW> ) { print OUT "out> $_" ; }
>close(OUT) || die "can't close OUT\n" ;
>while ( $_ = <INP> )
>  { print "<in $_" ; }
>close(INP) || die "can't close INP\n" ;

That's not the whole truth, try to replace 'sort' with 'cat'. See what happens?
Because 'sort' buffers the whole input before it writes it to the outgoing
pipe, you get trouble if the command does no buffering.

To really write and read independently you have to fork to split the process,
and it will look like:


$SIG{'CLD'} = 'IGNORE';                 # Don't leave Zombies

&open2(INP,OUT,'cat | head ') ;         # (open2 from above)

if( fork == 0 ) {               #CHILD
	close(INP) || die "can't close INP\n" ;
	open(PW,'/etc/passwd') || die "can't open /etc/passwd\n" ;
	while (<PW>) {
		print OUT "out> $_";
	}
	close(OUT) || die "can't close OUT\n" ;
	exit 0;
} else {                               #FATHER
	close(OUT) || die "can't close OUT\n" ;
	while (<INP>) {
		print "<in $_" ;
	}
	close(INP) || die "can't close INP\n" ;
}

	bye, Herwig
-- 
#\ Herwig Henseler (CS-Student) D-2930 Varel, Tweehoernweg 69, 04451/2107 /#
##|  UUCP/EuNet: henseler@uniol.uucp \###/ Zerberus: henseler@uniol.zer  |##
#/ One of these days I'm going to cut you into little pieces - Pink Floyd \#

henkp@ruuinf.cs.ruu.nl (Henk P. Penning) (08/29/90)

In article <3284@uniol.UUCP> henseler@uniol.UUCP (Herwig Henseler) writes:

> [ quotes original problem, my solution, and detects deadlock ]

>To really write and read independently you have to fork to split the process,
>and it will look like:

> [ fork ; child writes ; parent reads ]

This could also be achieved by prepending the program-to-exec with a
buffering process like "perl -e '@_ = <> ; print @_' |".

The problem is that this solution is inadequate in the situation where
the data that the main-prog sends to the sub-prog depends (also) on what
the sub-prog sends back.
I think there is no general solution to the deadlock problem.
To avoid it, you always have to be carefull with your buffering and flushing.

				===  HenkP  ===

-- 
Henk P. Penning, Dept of Computer Science, Utrecht University.
Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands.
Telephone: +31-30-534106
e-mail   : henkp@cs.ruu.nl (uucp to hp4nl!ruuinf!henkp)

mark@DRD.Com (Mark Lawrence) (08/29/90)

} In article <3284@uniol.UUCP> henseler@uniol.UUCP (Herwig Henseler) writes:
} > [ quotes original problem, my solution, and detects deadlock ]

henkp@ruuinf.cs.ruu.nl (Henk P. Penning) wrote:
} The problem is that this solution is inadequate in the situation where
} the data that the main-prog sends to the sub-prog depends (also) on what
} the sub-prog sends back.
} I think there is no general solution to the deadlock problem.
} To avoid it, you always have to be carefull with your buffering and flushing.

But it is alright in this particular situation.  As Herwig pointed out,
I'm depending on sort's behaviour, i.e. buffering all input before
spewing output.
-- 
mark@DRD.Com uunet!apctrc!drd!mark$B!J%^!<%/!!!&%m!<%l%s%9!K(B

worley@compass.com (Dale Worley) (08/29/90)

   X-Name: Herwig Henseler

   To really write and read independently you have to fork to split the process,
   and it will look like:

You can also use select() to wait on both the input and output sides
of the connection at the same time.  At least, I *think* it will work
-- if select says an output fd is ready, does that guarantee that you
can output a reasonable amount to it without blocking?

Dale Worley		Compass, Inc.			worley@compass.com
--
Happiness is being famous for your financial ability to indulge
in every kind of excess.