I want to recursively find all directories and files inside a path but want to exclude some other directories and files that match given patterns. I then want to execute further commands on the search result, maybe cp, mv, rsync or rm the result of find.

Suppose I want to prune entire node_modules, .git and build directory and also prune enhancers directory but would like to include in the search result a file with -name xyz.js that is present at unknown depth inside the enhancers directory.

With the command below I am able to achieve the desired result except include xyz.js file in the search result. The command is very long.

find . -path "./node_modules" -prune -o -path "./.git*" -prune -o -path "./build" -prune -o -path "*/enhancers" -prune -o -print | grep 'xyz.js'


In the schematic directory structure above |.. stands for unknown depth(level) inside the directory.

In the shown directory structure, I want to include all the files in the search result except,

  • the entire node_modules directory
  • entire .git directory
  • .gitignore file
  • entire enhancers directory, but include xyz.js file in search result

Is there a shorter way to do it? Major confusion is between regexp and glob patterns.

If I substitute ./.git* with ./\.git*, then also it works.

If I use more than one * in */enhancers, then also it works.

What pattern rules to search for files and directories are applicable in find command?

  • It sounds like an interesting problem, but I would find it easier to understand how to tackle it if there were a MWE to get me started. Could you give instructions for constructing a set of directories and files that would work as a test case, and what the expected output would be given those directories and files? – cryptarch Dec 13 '18 at 4:07
  • @cryptarch I have updated the question. – HarshvardhanSharma Dec 13 '18 at 6:49

If you want to be able to find any file inside enhancers, then you can't prune it. prune means find not descending in it at all so there's no chance it can find files in there. Here, you'd want not to prune those but exclude any file below them but the xyz.js ones.


find . \( \
          -path ./node_modules -o \
          -path './.git*' -o \
          -path ./build \
       \) -prune -o \( \
          ! -path '*/enhancers' ! -path '*/enhancers/*' -o \
          -name xyz.js \
       \) -print

Here on several lines for legibility.

Replace -print with -exec sh -c 'for file do something with "$file"; done' sh {} + if you want to do something with those files.

Or if you want to post-process the output, use -print0 and use the -z/--null/-0 options of grep/xargs/cut/sort (assuming GNU implementations or compatible) for them to work on NUL-delimited input.

  • The command is so long that it gets convoluted with high chance of typo in shell interactive mode. – HarshvardhanSharma Dec 13 '18 at 8:49
  • The result also includes the link .(current directory) in the search result. How to exclude .. Even if it is included, will it be an input to commands I wish to execute on search results like other search results? – HarshvardhanSharma Dec 13 '18 at 10:21
  • @HarshvardhanSharma, add a ! -name . before the -print/-print0/-exec if you want to exclude it. Some find implementations also have a -mindepth 1 which you could use instead. – Stéphane Chazelas Dec 13 '18 at 10:31
  • I get mv: cannot stat '[file]' : No such file or directory error for files and folders other than root directory of find search result, when I try to mv the contents of the search result with [script] | xargs 0 -n1 mv -t /Destination/Directory. I guess this is due to inclusion of root-directory in find search result ./root-directory which is moved first along with all its content. I want to sort of cut and paste to the destination, maintaining the directory structure. – HarshvardhanSharma Dec 13 '18 at 14:56

You can make a bash shellscript with two rsync command lines with filter rules,


rsync -Havn \
 --filter="- target" \
 --filter="- node_modules" \
 --filter="- .git" \
 --filter="- build" \
 --filter="- enhancers" \
 ./ target

rsync -Havn  \
 --filter="+ xyz.js" \
 --filter="+ */" \
 --filter="- *" \
 src target

It is fairly easy to edit this shellscript until it produces a nice output with -n which means 'dry run' only showing what is the intention.

When things look correct, you can remove -n from the two command lines and let rsync do its thing. Or maybe you are happy with the text output, and will save it to a file or pipe it somehow into other commands (to do other things than rsync-copying).

  • Why do I need to add \ for the command to span multiple lines? Isn't bash more flexible with where newlines can be added without need for \ after every line? – HarshvardhanSharma Dec 13 '18 at 9:05
  • @HarshvardhanSharma, Bash considers the command finished, when there is a newline unless you escape it with \. It is not more flexible. – sudodus Dec 13 '18 at 9:08

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.