Search & Find

Getting Started

Three different jobs, three tool families: find walks the filesystem live and can filter on any attribute, locate answers instantly from a prebuilt index, and grep searches inside files. fd, plocate, and rg are their modern, faster counterparts.

Find Files by Name

find needs a starting directory; -iname matches case-insensitively. fd searches the current directory by default, ignores hidden and git-ignored files, and uses regex patterns.
$ find . -iname "*report*"
$ find / -name "[fileName]" 2>/dev/null
$ fd [query]
$ fd -e pdf [query]
Searching from / prints permission errors for directories you cannot read; 2>/dev/null hides them.

Find by Type, Size, and Age

Tests combine freely: a number like -7 means "less than", +7 means "more than", a bare 7 means "exactly".
$ find . -type f -size +10M -size -100M
$ find . -type f -empty
$ find . -type d -empty
$ find . -mmin -60
$ find . -mtime -7
$ find . -mtime +30
TestDescription
-type f / -type dFiles / directories
-size +10MLarger than 10 MB
-mmin -60Content modified less than 60 minutes ago
-mtime -7Content modified within the last 7 days
-cmin / -ctimeStatus (permissions, owner) changed
-atimeLast accessed
-emptyEmpty file or directory
-user nameOwned by a user

Act on What You Find

-exec runs a command on every match; {} is replaced by the file name. Ending with + passes many files per invocation instead of one at a time. -delete removes matches directly.
$ find . -name "*.tmp" -delete
$ find . -name "*.sh" -exec chmod +x {} +
$ find . -type f -exec grep -l "TODO" {} +
Test destructive commands first: replace -delete or -exec rm with -print, check the list, then run it for real.

Indexed Search

locate finds path names instantly from an index instead of scanning the disk. The index is updated periodically by updatedb, so very new files may be missing. plocate is the faster modern implementation.
$ locate [query]
$ plocate [query]
$ sudo updatedb

Search File Contents

grep -r searches directories recursively; -n shows line numbers, -i ignores case, -l lists only file names. rg (ripgrep) is recursive by default, skips git-ignored files, and is much faster on big trees.
$ grep "[query]" [file]
$ grep -rni "[query]" [path]
$ grep -rl "[query]" [path]
$ rg "[query]"
$ rg -t py "[query]"
$ ag "[query]"
Search the output of any command by piping it through grep.
$ history | grep "[phrase]"
$ ps aux | grep "[processName]"

Fuzzy Finding

fzf filters any list interactively as you type. On its own it fuzzy-finds files; combined with other commands it becomes a universal picker.
$ fzf
$ vim $(fzf)
$ history | fzf
fzf's shell integration binds Ctrl+R (history), Ctrl+T (files), and Alt+C (cd into directory).

Find Commands

Where does a command live, and what is it really?
$ which [command]
$ whereis [command]
$ type [command]
$ command -v [command]
whereis also finds the man page and source. type is the shell's own view and correctly reports aliases, functions, and builtins.