Brian Sexton Brian Sexton - 5 months ago 12
Linux Question

Is there a way to specify exceptions to exclusions in the find command via Bash?

I would like to find all of the files within a directory and its subdirectories except for any settings files and anything in settings or dependency directories.

For example, I want to exclude from my results entire directories like .git, .idea, and node_modules as well as files like .DS_Store and config.codekit, but I want to include .gitignore.

What I want is something like the results of the following Git command, but including any untracked files and able to be easily and safely operated upon (e.g., to change permissions).

git ls-tree -r master --name-only


Here is what I have so far and although it is rather unwieldy it seems to mostly do what I want except for leaving out .gitignore:

find . -type f -not -name ".*" -not -name config.codekit -not -path "./.*" -not -path "./node_modules/*"


I have experimented with -prune without much success.

Is there a way to specify exceptions to exclusions in the find command via Bash—to say something like exclude all the things that match this pattern EXCEPT this thing or these things?

By the way, I am presently using OS X, but I also use Ubuntu and I plan to try Ubuntu on Windows when the Windows 10 Anniversary Update is generally available, so ideally I would like to have a command that works across all of those.

Thank you in advance for any solutions, insights, or optimizations!

Update

Thanks to help from gniourf-gniourf, I have revised my command. This seems to do what I wanted:

find . -type f \! -name ".*" \! -name config.codekit \! -path "./.*" \! -path "./node_modules/*" -o -name .gitignore

Answer

A quick example first: to find all files, pruning the .git directories and ignoring the .DS_Store files:

find . -name .git -type d \! -prune -o \! -name .DS_Store -type f

For example, I want to exclude from my results entire directories like .git, .idea, and node_modules as well as files like .DS_Store and config.codekit, but I want to include .gitignore.

find . \( -name .git -o -name .idea -o -name node_modules \) -type d \! -prune -o \! -name .DS_Store \! -name config.codekit -type f

When building your command, make sure you stick with the POSIX standard: it's a guarantee that your command will work on any (POSIX compliant) system. For example, -not is not POSIX compliant: use ! instead (you'll have to escape it so as to not clash with your shell history expansion).


Is there a way to specify exceptions to exclusions in the find command via Bash—to say something like exclude all the things that match this pattern EXCEPT this thing or these things?

Find files, excluding everything (pattern *) except the files one and two:

find . \( \! -name '*' -o -name one -o -name two \) -type f