Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I cant 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 cant 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?

share|improve this question
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? –  Volker Siegel Apr 17 at 5:43

4 Answers 4

up vote 2 down vote accepted

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.

share|improve this answer

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.
share|improve this answer
    
That still descends into the old directories, you may want to use -prune here to avoid that. –  Stéphane Chazelas Apr 17 at 7:14

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.

share|improve this answer
    
-path '*/old' is the same as -name old –  Stéphane Chazelas Apr 17 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... –  Volker Siegel Apr 17 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! –  Volker Siegel Apr 17 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". –  Volker Siegel Apr 17 at 7:47
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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.