3

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.

1
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.

  • 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
0

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... :-)

  • 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

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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