
The find command is a powerful utility in Unix-based systems for searching files in a directory hierarchy. However, when working in a Git repository, it often returns many files that should be ignored due to the .gitignore file.
Wouldn’t it be great if we could extend the find command to exclude the paths specified in .gitignore? With this ifind.sh shell script, we can!
Part of the common-utils utilities, this script enhances find‘s capabilities while also ignoring “noisy” directories that we usually want to exclude from the search.
How Does this Work?
The script uses find‘s ability to exclude paths, by negating (-not) the path-matching operator (-path), builds the command parsing an optional .gitignore and executes it at the end.
Unfortunately, unlike tar there is no simple --exclude option in find to make it parse directly a file with a list of exclusion patterns, so we have to build it path-by-path.
Specifying the Search Directory
First, the script sets the search directory. If no argument is supplied when the script is invoked, the current directory (.) is used by default.
declare -r search_dir=${1:-.}
Constructing the Initial Find Command
The initial find command string is built with the search directory and -type f. The -type f option tells find to look for regular files only.
cmd="find ${search_dir} -type f"
Excluding Noisy Directories
The script then excludes common “noisy” directories such as .git, .run, .idea, .cache. These directories are often excluded from search results as they don’t contain useful code files.
patterns=('.git' '.run' '.idea' '.cache')
for p in "${patterns[@]}"; do
cmd+=" -not -path ${search_dir}/$p/'*'"
done
Reading from.gitignore
The script then looks for a .gitignore file in the current directory or the search directory. If such a file exists, the script reads each line from the file.
gitignore=$((test -f ./.gitignore && echo './.gitignore') || \
(test -f "${search_dir}/.gitignore" && echo "${search_dir}/.gitignore"))
Incorporating .gitignore Exclusion Patterns
This script only includes directory exclusion patterns from the .gitignore file when constructing the find command, skipping exclusions themselves (lines starting with !) and comments.
File-specific or non-directory exclusion patterns are ignored:
if [[ -f ${gitignore} ]]; then
while read -r pattern; do
if [[ -n "$pattern" && ! ( "$pattern" =~ '^#' || "$pattern" =~ '^!' ) \
&& "$pattern" =~ '/$' ]]; then
cmd+=" -not -path '$search_dir/$pattern*'"
fi
done < ${gitignore}
fi
Executing the Find Command
After the find command is fully constructed, the script finally executes it.
eval "$cmd"
Example usage
This works both in a directory which contains .gitignore:
$ cd common-utils
~/Development/common-utils
$ ifind
./images/common-utils-small.jpeg
./Makefile
./install.sh
...
./head.html
./templates/Makefile-template
./templates/go-make-version-tag.sh
./templates/commons.cmake
compare with the output of simply running find .
and also from another directory, which does not contain the .gitignore (worth noting that if the directory contains one that one will be used):
$ cd Documents
~/Documents
$ ifind ~/Development/common-utils
/Users/marco/Development/common-utils/images/common-utils-small.jpeg
/Users/marco/Development/common-utils/Makefile
/Users/marco/Development/common-utils/install.sh
...
/Users/marco/Development/common-utils/manifest.json
/Users/marco/Development/common-utils/head.html
/Users/marco/Development/common-utils/templates/Makefile-template
/Users/marco/Development/common-utils/templates/go-make-version-tag.sh
/Users/marco/Development/common-utils/templates/commons.cmake
Conclusions
This ifind.sh script makes the find command more intelligent by excluding irrelevant paths specified in the .gitignore file, helping you efficiently find the files you need, amid a sea of project files.
This is available by installing common-utils Release 0.10.5 and later.
Happy finding!





Leave a comment