[net.unix] creating pipes in find

tcs%usna.uucp@BRL-BMD.ARPA (09/25/84)

How do I build a pipe within the exec portion of find?
Example:
	find /etc -name printcap -exec cat {} | lpr \;

I've tried lots of combinations of escaped parens, exec'ing
the shell, etc and nothing works.
I have gotten this to work:
	find /tmp -name tobuy -exec junk1 {} \;

junk1:
	cat $1 | more

But I'd rather know how to do it without having another file
lying around.

If it can be done, please explain the combination of
escapes, etc you use make it work.  Thanks.
	-tcs
	Terry Slattery	  U.S. Naval Academy	301-267-4413
	ARPA: tcs@brl-bmd     UUCP: decvax!brl-bmd!usna!tcs

rpw3@redwood.UUCP (Rob Warnock) (09/27/84)

[ Wizards, be patient. This IS net.unix, ne? ]

+---------------
| How do I build a pipe within the exec portion of find? Example:
|	find /etc -name printcap -exec cat {} | lpr \;
| I've tried lots of combinations of escaped parens, exec'ing
| the shell, etc and nothing works...
| 	Terry Slattery	  U.S. Naval Academy	301-267-4413
+---------------

I have never gotten it to work either (except like your "junk" auxiliary
shell script), but the following neat/ugly/cute/hacky method sprang to mind
one day as I was reading Kernighan & Pike [pp 86-87ff, see also "pick"]:

	$ for i in `find . <pattern> -print`
	> do echo $i		# so we can see what we're doing
	> cat -v $i | lpr	# ...or anything you want at all...
	> done

For C-shell users, that's:

	% foreach i (`find . <pattern> -print`)
	? echo $i	# so we can see what we're doing
	? cat -v $i | lpr
	? end

This lets you do more than one thing to the file, as well. Normally, the
"{}" token can be used exactly once, so things like "mv {} {}-" don't work.
Here, you can play games like "mv $i olddir/`basename $i`.old", and such.

K & P also show an example of using "grep" to select files by their contents.
Since this is USENET, I will make it news-oriented (let $SN be your spool dir):

$ for i in `grep -l '^Subj.*[aA]pple.*[sS]haft' \`find $SN/net/micro -print\``
> do echo $i
> pr -h Apple-Shaft-Articles $i | lpr
> done

This will print each article in "net.micro.all" that contains "Apple...Shaft"
in the subject line, as in a recent set of postings. (Surprise! The upgrade
controversy raged across several groups!)

[I tried this. It worked. However, I had just done a severe "expire". So,...]

WARNING: Make sure that the "find" is not going to give you so much stuff
that your shell (or your system) blows up from an over-large argument list.
(That is, DON'T do "for i in `find / -print`")

An example of practical uses includes going through the news spool area
fixing up ownership, group, and modes of any directories that created by
"root" or "uucp" instead of "news" (seems to occasionally happen):

	$ for i in `find /usr/spool/news ! -user news -print`
	> do ls -adl $i		# show the culprit
	> /etc/chown news $i
	> /etc/chgrp news $i	# for those who don't have chowngrp
	> chmod 755 $i		# or whatever protection you run with
	> done

I'm sure you can think of lots of uses.

WARNING#2: If the command string gets so long (more than 2-3 lines) that
your error rate typing it wipes out the savings you got from using "immediate
mode", go back and edit a real shell script to do the work, test it on a
couple of files, then feed it to a "find ... -exec <script> \;". It's safer.

Rob Warnock

UUCP:	{ihnp4,ucbvax!amd}!fortune!redwood!rpw3
DDD:	(415)369-7437
Envoy:	rob.warnock/kingfisher
USPS:	Suite 203, 4012 Farm Hill Blvd, Redwood City, CA  94061

merlyn@sequent.UUCP (09/28/84)

#endif BUG

rpw3@redwood.UUCP sez:
>> WARNING: Make sure that the "find" is not going to give you so much stuff
>> that your shell (or your system) blows up from an over-large argument list.
>> (That is, DON'T do "for i in `find / -print`")

A way around this is to do something like:

	find <dirs> <selection> -print |
	while read i
	do
		<$i commands>
	done

Works real slick.  Example:

	find / -size 0 -print |
	while read i
	do
		ls -ldg $i
	done

does an ls on all zero-length files on the system.

-- A particularly personal and original observation from the thought-stream of
Randal L. ("your favorite phrase here") Schwartz, esq. (merlyn@sequent.UUCP)
	(Former Official Legendary Sorcerer of the 1984 Summer Olympics)
Sequent Computer Systems, Inc. (503)626-5700 (sequent = 1/quosine)
UUCP: {decwrl,ogcvax,pur-ee,rocks34,shell,unisoft,vax135,verdix}!sequent!merlyn

fair@dual.UUCP (Erik E. Fair) (10/02/84)

>> From: tcs%usna.uucp@BRL-BMD.ARPA
>> Newsgroups: net.unix
>> Subject: creating pipes in find(1)
>> Date: Tue, 25-Sep-84 11:51:16 PDT
>> 
>> How do I build a pipe within the exec portion of find?
>> Example:
>> 	find /etc -name printcap -exec cat {} | lpr \;
>> 
>> I've tried lots of combinations of escaped parens, exec'ing
>> the shell, etc and nothing works.
>> I have gotten this to work:
>> 	find /tmp -name tobuy -exec junk1 {} \;
>> 
>> junk1:
>> 	cat $1 | more
>> 
>> But I'd rather know how to do it without having another file
>> lying around.
>> 
>> If it can be done, please explain the combination of
>> escapes, etc you use make it work.  Thanks.
>> 	-tcs
>> 	Terry Slattery	  U.S. Naval Academy	301-267-4413
>> 	ARPA: tcs@brl-bmd     UUCP: decvax!brl-bmd!usna!tcs

I think what you want is something like this:

find / -perm -4000 -exec ls -ls {} \; | Mail -s "Daily SUID Program Report" root

Note the escaped semi-colon? That's part of the argument list to find(1).
That's what terminates an exec list. Since the semi-colon is escaped,
the shell (whichever one you use) will not use it to terminate the command.
Clear?

	Erik E. Fair	ucbvax!fair	fair@ucb-arpa.ARPA

	dual!fair@BERKELEY.ARPA
	{ihnp4,ucbvax,hplabs,decwrl,cbosgd,sun,nsc,apple,pyramid}!dual!fair
	Dual Systems Corporation, Berkeley, California

fcy@iham1.UUCP (Fred Yankowski) (10/03/84)

In UNIX System V Release 1, 'find' processes its '-exec' arguments
with 'execvp(II)', not 'system(III)'.  This means that *no* shell
processing is done:  '|', '&', ';', '(', etc. are not interpreted by
the shell.  The command

	find / -type f -exec cat {} \| lpr \;

will then execute

	cat "file" "|" "lpr"

for each file "file" found by 'find' (got that?).
That is, 'cat' is executed with three arguments: "file", "|", and "lpr".

One handy idiom for processing files found by 'find' is:

	find dir <find arguments> -print | xargs <process>

For example:

	find / -type f -print | xargs -n1 lpr

does what seems to be desired in the first (faulty) 'find' command
above.  Even better is

	find / -type f -print | xargs lpr

in which 'xargs' repeatedly gathers as many file names as fit in an
internal buffer and executes 'lpr' against each such list of file
arguments.  The improvement is in execution time, since 'lpr' is
fork/execed once for each list of files, rather than for each file
alone.

Similarly, cleaning out a directory is better accomplished with

	find . -type f -print | xargs rm

rather than

	find . -type f -exec rm {} \;


    Fred Yankowski ::: AT&T Bell Laboratories ::: ihnp4!iham1!fcy
			  IH 6B-216  x6902

johnl@cca.UUCP (10/04/84)

#R:sri-arpa:-1237800:ima:19700001:000:496
ima!johnl    Oct  3 10:25:00 1984

> How do I build a pipe within the exec portion of find?
> Example:
> 	find /etc -name printcap -exec cat {} | lpr \;

The short answer is that you can't -- find uses an exec() call to
run what it runs, and exec won't create pipes.  Any "|" characters are
handled by the shell.  In the Bourne shell, though, you can do stuff like
this:

	find /etc -name foo - print | while read fn do
	 cat $fn | lpr
	done

I suppose that there's some way to do that in the C shell, too.

John Levine, ima!johnl