shell commands - exclusion
Jonathan McKeown
jonathan+freebsd-questions at hst.org.za
Sun Feb 8 04:08:39 PST 2009
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 at pcbsd% find .
(No expression). Find adds a -print, so this is the same as the next one:
> chrisw at 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 at pcbsd% find . -print -o -prune dir1
> find: dir1: unknown option
-prune doesn't take an argument, so dir1 is a syntax error.
> chrisw at 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 at 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 at 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
More information about the freebsd-questions
mailing list