4

I can't figure out how the find -not works.

Let's say that I would like to locate all .git dirs in the tree.

find -type d -iname .git

No problems, but then let's say that I have some dirs that I don't like to be included, in question they can be called "old" and "backup".

I could pipe to grep -v and that will work just fine.

find -type d -iname .git | grep -v old | grep -v backup

But when I browsed the man page for find I noticed that there is a -not, but I can't figure out how it is intended to work.

I tried like this, but he does not exclude the old dir.

find -type d -iname .git -not -iname old 

How does the find -not work? Can I use it for this find of problem?

1
  • 2
    It would be good to specify what exactly you try to find - do you really want to exclude and include what the second example does? Apr 17, 2014 at 5:43

4 Answers 4

2

find has a little bit of sophistication to deal with this case:

If the expression contains no actions other than -prune, -print is performed on all files for which the expression is true.

So explicitly print just the parts you want:

find -type d \( -iname old -prune -o -iname backup -prune -o -iname .git -print \)

avoids searching the old and backup trees at all.

2

You could exclude the directories by specifying -path:

find . -type d \( -iname .git ! -path "*/old/*" ! -path "*/backup/*" \)

Note that -not in GNU find is the same as ! but is not POSIX compliant.

Quoting from man find:

   ! expr True  if  expr  is false.  This character will also usually need
          protection from interpretation by the shell.

   -not expr
          Same as ! expr, but not POSIX compliant.
1
  • That still descends into the old directories, you may want to use -prune here to avoid that. Apr 17, 2014 at 7:14
2

You need the -prune option of find, it's really pretty tricky. It requires to use -path instead of -name too.

I think you want to use this:

find . -type d \( -path '*/old' -prune -o -iname '.git' -print \)

The effect of the -not is implemented by

  • matching old "old", and the donig nothing
  • or (using option -o)
  • matching ".git" and printing the name
  • or (by default)
  • doing nothing (because the normal default action of find, an implicit -print at the end of the command line, is disabled, if -print is used already)

I recognize that other variants, while looking more complicated on the command line, may be easier to use, for example because the use of a different logic expression, and the changing of the default action.
On the other hand, the variants involving -prune are easier to generalize for more complex cases, when other syntax will be hard to handle just because it gets too long to read.

4
  • -path '*/old' is the same as -name old Apr 17, 2014 at 7:18
  • @StephaneChazelas Oh, I thought in relation to -prune, it's explicitly needed to match the whole path. But that may apply only for some other case... Apr 17, 2014 at 7:21
  • @StephaneChazelas Re your edit: leaving out the dir "." was not intended, of course. And yes, looks like it did not check the '.git' to be a directory, I assume; Thanks! Apr 17, 2014 at 7:31
  • @StephaneChazelas regarding whether -path is needed instead of -name with -prune, I was thinking of the normal case where we want to exclude a certain directory, not all, recursively, that are named "old". Apr 17, 2014 at 7:47
1
find -type d -iname .git -not -iname old 

This matches directories which are called .git and not called old. Since a directory which is called .git cannot be called old, the -not -iname old part can be removed without changing the meaning of the command.

-not says “don't match”, but not “don't recurse into”. There's a different way to say “don't recurse into”: it's an action called -prune. The following command means “traverse all directories recursively (find), but stop at directories called old (-iname old -prune), and print directories called .git”:

find -iname old -prune -o -type d -iname .git -print

This is parsed as ( -iname old -prune ) -o ( -type d -iname .git -print ). Find is told to look for files that meet the conditions -iname old or (-o) -type d -iname .git. When a file meets the first condition, the action -prune (prune from the traversed tree, i.e. don't recurse) is executed. When a file meets the second condition, the action -print is executed. As printing is the default action, you can omit -print.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .