Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 08 Feb 2009 23:30:26 +0000
From:      Chris Whitehouse <cwhiteh@onetel.com>
To:        Jonathan McKeown <jonathan+freebsd-questions@hst.org.za>
Cc:        freebsd-questions@freebsd.org
Subject:   Re: shell commands - exclusion
Message-ID:  <498F6B12.5000307@onetel.com>
In-Reply-To: <200902081412.15509.jonathan%2Bfreebsd-questions@hst.org.za>
References:  <332f78510902040635k6675a9b6u434879b42c66a579@mail.gmail.com>	<ae4324ed0902041905p4497c155u5d474533bbd5151f@mail.gmail.com>	<498B8A7F.2060906@onetel.com> <200902081412.15509.jonathan%2Bfreebsd-questions@hst.org.za>

next in thread | previous in thread | raw e-mail | index | archive | help
Jonathan McKeown wrote:
> On Friday 06 February 2009 02:55, Chris Whitehouse wrote:
>> I think you should be able to do it with a combination of -prune and
>> -delete (or -exec rm -rf {} \; ) on a find command. Substitute your
>> other commands for rm -rf in the -exec above.
>>
>> I would give you a working example except I can't figure out the syntax
>> for -prune. Examples from google don't seem to work in (my) FreeBSD.
> 
> [skip to the end for a simple answer without the lengthy exposition]
> 
> find(1) can be confusing, especially if you think of the ``actions'' 
> ( -print, -exec and -delete plus their variants like -ls and -ok ) as 
> something different from the ``tests'' ( -name and so on), or if you don't 
> take account of the evaluation order.
> 
> A find expression comprises a number of what the manpage calls primaries, each 
> of which evaluates as true or false. (It may also have a side-effect, 
> like -print whose side-effect is to print the name). Primaries can be 
> combined with -and (which is usually implied) or -or. Where -and and -or both 
> occur, find will group the -anded primaries together before evaluation. 
> Taking one of your examples below,
> 
> find . -print -or -prune -name dir1
> 
> this is grouped as
> 
> find . -print -or \( -prune -and -name dir1 \)
> 
> find will then evaluate the whole expression from left to right for each 
> pathname in the tree it's looking at, stopping within each set of (implied) 
> parentheses and within the overall expression as soon as it can determine 
> truth or falsehood. (This is what's referred to in programming as 
> short-circuiting in boolean expressions).
> 
> If primaries are linked by -and, find can stop at the first one that's false, 
> knowing the expression is false; if they're linked by -or it can stop at the 
> first one that's true, knowing the expression is true. Otherwise it has to 
> evaluate the whole expression.
> 
> Before it does this, though, find checks for side-effects. If there isn't a 
> side-effect anywhere in your expression, find will put brackets round the 
> whole expression and a -print after it.
> 
> Looking at your examples:
> 
>> chrisw@pcbsd% find .
> 
> (No expression). Find adds a -print, so this is the same as the next one:
> 
>> chrisw@pcbsd% find . -print
>> .
>> ./test.mov
>> ./test.mpg
>> ./dir1
>> ./dir1/file1
>> ./dir1/file2
>> ./file3
> 
> -print is always true so the expression is true for each name - they get 
> printed as a side-effect.
> 
>> chrisw@pcbsd% find . -print -o -prune dir1
>> find: dir1: unknown option
> 
> -prune doesn't take an argument, so dir1 is a syntax error.
> 
>> chrisw@pcbsd% find . -print -o -prune -name dir1
> 
> find evaluates the print, which prints each name as its side-effect. -print 
> evaluates as true. Since it's in an -or, find can stop there, so it never 
> sees the second expression ( -prune -and -name dir1: the -and is implicit).
>> .
>> ./test.mov
>> ./test.mpg
>> ./dir1
>> ./dir1/file1
>> ./dir1/file2
>> ./file3
> 
>> chrisw@pcbsd% find . -print -o -name dir1 -prune
> 
> Same again: find stops after the -print which is always true, and ignores 
> the -name dir1 -and -prune.
> 
>> chrisw@pcbsd% find . -name "*" -o -name dir1 -prune
> 
> None of these primaries has a side-effect, so find rewrites this internally as
> 
> find . \( -name "*" -or -name dir1 -prune \) -print
> 
> -name "*" is always true, so find can ignore everything after the -or up to 
> the parenthesis. Because the first expression is true, and the parens are 
> followed by (an implied) -and, find has to evaluate the -print, which is 
> always true, so the whole expression is always true and it always prints the 
> name as a side-effect.
>> .
>> ./test.mov
>> ./test.mpg
>> ./dir1
>> ./dir1/file1
>> ./dir1/file2
>> ./file3
> 
> What you need is an expression with two outcomes: a -prune for some names and 
> a -print for others. That tells you you need an -or, and the -print must come 
> after it because it's always true. Before the -or, -prune is always true so 
> you need some sort of testing primary before the -prune.
> 
> That gives you
> 
> find . -name dir1 -prune -or -print
> 
> Jonathan
> _______________________________________________
> freebsd-questions@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-questions
> To unsubscribe, send any mail to "freebsd-questions-unsubscribe@freebsd.org"
> 
Thank you for this excellent answer! Now reading the man page begins to 
make sense.

Chris



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?498F6B12.5000307>