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 use a find command to search for all files with a given extension within the current subdirectories ignoring certain directory path:

find -L . \( -wholename "*/ignoredPath" -o -wholename "*/ignoredPath2" \) -prune -o -name "*.ext"

This is working pretty well but now I want to ignore another path except one precise subdirectory in it. How to do this without listing all the subdirectories to ignore?

For instance if I have the following folder tree:

*/base/pathToIgnore1
*/base/subdir/requiredPath
*/base/subdir/pathToIgnore2
*/base/subdir/pathToIgnore3
*/base/pathToIgnore4

How can I write the find command ignore all base subdirectories except the path */base/subdir/requiredPath?

I tried something like

find -L . \( -wholename "*/ignoredPath" -o -wholename "*/ignoredPath2" -o \( -wholename "*/base" -a ! -wholename "*/base/subdir/requiredPath" \) \) -prune -o -name "*.ext"

But it did not work, all the base subdirectories are ignored.

share|improve this question

2 Answers 2

up vote 1 down vote accepted
find -L . \
  \( -type d \
    \( -path "*/ignore" -o -path "*/indeed" -o \
      \( -path "*/subdir/*" ! -path "*/subdir/save" \
      \) \
    \) \
  \) \
  -prune -o -print

If you want extra filter (like -name "*.ext") you have to put that right before -print. The last part then looks like this

  -prune -o \( -name "*.ext" \) -print

Note that I changed the names for easier writeability and readability. Names starting with i are to be ignored. Names starting with s are to be shown. Names ending in file are files.

My tree looks like this:

$ find -printf "%y %p\n"
d .
d ./base
d ./base/subdir
d ./base/subdir/inform
f ./base/subdir/inform/imagefile
d ./base/subdir/isolate
f ./base/subdir/isolate/individualfile
f ./base/subdir/whatwhatinthefile
d ./base/subdir/save
f ./base/subdir/save/soundfile
f ./base/superfile
d ./base/indeed
f ./base/indeed/itemfile
d ./base/show
f ./base/show/startfile
d ./base/ignore
f ./base/ignore/importantfile

The output of the above command:

.
./base
./base/subdir
./base/subdir/whatwhatinthefile
./base/subdir/save
./base/subdir/save/soundfile
./base/superfile
./base/show
./base/show/startfile

Note that whatwhatinthefile in base/subdir. If you do not want the files in base/subdir then you have to exlude them explicitly. I tried but the command line became too ugly.

Depending on your use case it might be easier to define a shell function like this:

contrivedfind() {
  find -L . \
    \( -type d \
      \( -path "*/ignore" -o -path "*/indeed" -o -path "*/subdir" \
      \) \
    \) \
    -prune -o -print
  find -L ./base/subdir/save
}

Output is now:

.
./base
./base/superfile
./base/show
./base/show/startfile
./base/subdir/save
./base/subdir/save/soundfile

The only other thing different from before now is the missing ./base/subdir entry. But I think that does not matter in your case because you want to filter for files anyway.

As before you have to put any extra filter before the -print in the first find and this time also at the end of the second find.

share|improve this answer
    
I did not tried your command yet but your example does not cover my use case. Based on the tree example you gave, what I am looking for is a rule to ignore everything within base except the specific base/subdir/save subtree. –  greydet May 14 '13 at 21:23
    
@greydet see updated answer –  lesmana May 14 '13 at 21:41
    
You are right it is way more readable to make it in two commands! Thanks, I didn't think about this easy solution. –  greydet May 15 '13 at 7:07

You have to prevent that */base itself is matched:

Edit 1:

find -L . -type d \
  \( -wholename "*/ignoredPath" -o -wholename "*/ignoredPath2" -o \
  \( -wholename "*/base/*" -a \
  \( ! -wholename "*/base/subdir" -a ! -wholename "*/base/subdir/*" \) \) -o \
  \( -wholename "*/base/subdir/*" -a \
  \( ! -wholename "*/base/subdir/requiredPath" -a ! -wholename "*/base/subdir/requiredPath/*" \) \) \) \
  -prune -o -print

This is a bit finicking: It prevents accidental matches of directories like subdir2 which probably don't exist. But you never know... :-)

share|improve this answer
    
This also prints out directory names on my Debian, not just the .ext files. –  terdon May 14 '13 at 11:44
    
@terdon Funny. You mean directory names != "*.ext" I assume. –  Hauke Laging May 14 '13 at 11:53
    
@terdon Your edit: I need the "\", too. I made the mistake of copying the OP code in my answer. But of course the find command line can be broken into several lines by "\". That is shell stuff and has nothing to do with find. –  Hauke Laging May 14 '13 at 11:58
    
You're quite right, the \ was not working because the ( were not escaped. You can check by running your original command. I changed it back since it is indeed correctly broken with the escaped parentheses. Sorry. As for the directory names, no I mean it prints directories, not only files with .ext, for example, ` ./ignoredPath2`. –  terdon May 14 '13 at 12:51
    
This does not seem to work for me. I don't want to include the whole subdir but only subdir/requiredPath and its whole directory tree. When I adapt the proposed command with -wholename "*/base/subdir/*" -a ! -wholename "*/base/subdir/requiredPath" it does not find anything within requiredPath but find stuff within base/pathToIgnore1 and base/pathToIgnore4 –  greydet May 14 '13 at 13:00

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.