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>