To: pm-help@listserv.oit.unc.edu Subject: perl tip: filtering output, yourself! Date: Thu, 21 Jan 1999 13:42:50 -0500 Message-ID: <9226.916944170@aur.alcatel.com> From: "John M. Klassa" I first wrote this up for the Alcatel Perl User's Group, which was really just a mailing list with about fifteen people on it. :-) Since this list has gotten no traffic, I thought I'd throw this out as a way to encourage other to post questions and/or tips. Hope to see you tonight, John ------- Forwarded Message Date: Thu, 03 Sep 1998 10:17:11 -0400 From: "John M. Klassa" To: alcapug Subject: [AlcaPUG]: perl tip: filtering output, yourself! [ Based on the Perl Cookbook's recipe number 16.5... ] Let's say you want to generate some output from script A... What you'd really like, though, is to seamlessly, transparently pipe that output through trivial, throw-away, one-liner script B. You'd really rather not have to "install" script B and write a wrapper for A so that its output is sent to the right place... You'd like everything to be encapsulated into script A, and transparent. You might want to do something like this (pipe A's output through B) if you don't want to have to be terribly concerned with all the places where A is doing output -- you just want to use 'print', and let the transformations handle themselves. Let's say that script B replaces all instances of 'foo' with 'bar', and looks like this: # script B while (<>) { s/foo/bar/g; print } To do this, use the flavor of 'open' that does an implicit 'fork', and use it to replace A's STDOUT. STDIN for the child (where we'll stick B) is the parent's new STDOUT; STDOUT for the child is the parent's original STDOUT. At this point, you can 'print' in the parent, and the result is sent to the child (as STDIN), where the child can do whatever it likes to it before shipping the result out on STDOUT. Quoting from 'perlfunc': If you open a pipe on the command "-", i.e., either "|-" or "-|", then there is an implicit fork done, and the return value of open is the pid of the child within the parent process, and 0 within the child process. (Use `defined($pid)' to determine whether the open was successful.) The filehandle behaves normally for the parent, but i/o to that filehandle is piped from/to the STDOUT/STDIN of the child process. Putting it all together, then, you've got: # script A (which now includes script B, below) &transform_foo_to_bar; print while (<>); sub transform_foo_to_bar { return if $childpid = open STDOUT, '|-'; # parent returns die "couldn't fork child: $!\n" unless defined $childpid; while () { s/foo/bar/g; print } # the old "B" script exit; # terminate child } You can even combine output filters... Because the parent's STDOUT becomes the child's STDOUT, you can actually insert a new filter into the pipeline ahead of existing filters (so that the end result is LIFO). Pictorially: Originally: PARENT ------ in=STDIN out=STDOUT After first filter is installed: PARENT CHILD1 ------ ------ in=STDIN in=STDOUT(1) out=STDOUT(1) out=STDOUT Data flows into the parent on STDIN, out of the parent and into the first child on STDOUT(1) and back out of the first child on STDOUT. After the second filter is installed: PARENT CHILD2 CHILD1 ------ ------ ------ in=STDIN in=STDOUT(2) in=STDOUT(1) out=STDOUT(2) out=STDOUT(1) out=STDOUT Data flows into the parent on STDIN, out of the parent and into the *second* child on STDOUT(2), out of the second child and into the *first* child on STDOUT(1) and out of the first child on STDOUT. You can insert arbitrarily many filters (within the limits of what your machine can handle, in terms of process resources, of course). ------- End of Forwarded Message -- John Klassa / Alcatel / Raleigh, NC, USA / $perl_monger{Raleigh}[0] / <><