How to Find Files and Directories in Linux
Linux has several search tools, and each answers a different question:
findwalks the filesystem live and filters by anything: name, type, size, age, owner, permissions.locatesearches a prebuilt index. It is instant, but only as fresh as the last index update.fdis a faster, friendlierfindfor interactive use.grep -randrgsearch inside files, not file names.command -vandtypefind programs in yourPATH.
find basics
The general form is find WHERE TESTS ACTION. Without an action, find prints the matching paths.
find . -name 'config.yml' # current directory and below
find /etc -name '*.conf' # by pattern
find /var/log -iname '*error*' # case-insensitive
find / -name 'nginx.conf' 2>/dev/null # whole system, hide "Permission denied"
Always quote the pattern. Unquoted, the shell expands *.conf against the current directory before find sees it, and you get wrong results or a paths must precede expression error.
Searching / as a regular user produces a wall of "Permission denied" messages. Send them to /dev/null as above, or run the search with sudo. Add -xdev to stay on one filesystem and skip /proc, /sys and network mounts.
Find a directory
-type limits results to one kind of entry: f for files, d for directories, l for symlinks.
find / -type d -name 'my-app' 2>/dev/null
find ~ -type d -iname '*backup*'
find . -maxdepth 2 -type d # only two levels deep
find /srv -type d -empty # empty directories
-maxdepth goes right after the starting path. It is the easiest way to keep a search on a large tree fast.
Filter by size, time, owner and permissions
find /var -type f -size +500M # larger than 500 MiB
find . -type f -empty # empty files
find /var/log -type f -mtime +30 # modified more than 30 days ago
find ~/projects -type f -mtime -1 # modified in the last 24 hours
find /etc -type f -mmin -60 # modified in the last hour
find . -newer deploy.log # newer than a reference file
find /home -user deploy # owned by a user
find /srv -nouser # owner account no longer exists
find / -xdev -type f -perm -4000 # setuid binaries
find . -type f -perm -0002 # world-writable files
-mtime counts whole 24-hour periods and drops the fraction, so -mtime +30 effectively means 31 days or older. Use -mmin when you need precision.
Combine conditions
Tests are joined with AND by default. Use -o for OR, ! to negate and escaped parentheses to group:
find . -type f \( -name '*.jpg' -o -name '*.png' \)
find . -type f ! -name '*.log'
find . -path ./node_modules -prune -o -type f -name '*.js' -print
The last line skips node_modules entirely. -prune stops find from descending into it, and the explicit -print at the end keeps the pruned directory itself out of the output.
Do something with the results
find /tmp/build -type f -name '*.o' -delete
find . -type f -name '*.sh' -exec chmod +x {} +
find /var/log/my-app -name '*.log' -mtime +7 -exec gzip {} +
find . -name '*.tmp' -print0 | xargs -0 rm -f
-exec ... {} +passes many files to one command, which is fast.-exec ... {} \;runs the command once per file.-print0 | xargs -0handles file names with spaces and newlines safely.-okworks like-execbut asks for confirmation on every file.
Before deleting anything, run the same command with -print instead of -delete and read the list. Keep -delete last: find evaluates left to right, so find . -delete -name '*.tmp' deletes everything and only then checks the name.
A typical cleanup job:
find /backups -name 'db-*.sql.gz' -mtime +14 -print # check first
find /backups -name 'db-*.sql.gz' -mtime +14 -delete
locate: instant search from an index
locate reads a database of file names instead of walking the disk, so it answers in milliseconds even on large systems. It is often not installed by default.
sudo apt install plocate # Debian, Ubuntu
sudo dnf install plocate # Fedora, RHEL 10
sudo dnf install mlocate # RHEL 8 and 9
sudo updatedb # build or refresh the index now
locate nginx.conf
locate -i readme # case-insensitive
locate -r '/nginx\.conf$' # regular expression, exact file name
The index is refreshed once a day by a timer or cron job. Files created since then will not show up until you run sudo updatedb, and deleted files may still appear. When freshness matters, use find.
fd: a friendlier find
fd has short syntax, colored output and is much faster on big trees.
sudo apt install fd-find # Debian, Ubuntu (binary is called fdfind)
sudo dnf install fd-find # Fedora; EPEL on RHEL
On Debian and Ubuntu, add alias fd=fdfind to ~/.bashrc. Then:
fd config # name contains "config"
fd -e yml # by extension
fd -t d backup # directories only
fd -H -I secret # include hidden and git-ignored files
By default fd skips hidden files and anything listed in .gitignore. That is why it is fast, and also why it sometimes misses a file that find would show.
Search inside files
To find which files contain a string, search the content with grep. See How to use grep for the details.
grep -rl 'DATABASE_URL' /etc /srv/my-app
rg -l 'DATABASE_URL' # ripgrep, if installed
Find a command or program
command -v kubectl # path the shell will run
type -a python3 # every match, including aliases and functions
whereis nginx # binary, man pages, sources
dpkg -S /usr/bin/curl # which package owns a file (Debian, Ubuntu)
rpm -qf /usr/bin/curl # same on the RHEL family
which also works, but it does not know about aliases and shell functions, so command -v and type are more reliable.
Quick reference
find . -name 'file.txt' # by exact name
find / -iname '*.conf' 2>/dev/null # case-insensitive, whole system
find / -type d -name 'my-app' 2>/dev/null # a directory
find . -type f -size +100M # big files
find . -type f -mtime -1 # changed in the last day
find . -name '*.tmp' -print # check before -delete
find . -name '*.sh' -exec chmod +x {} + # act on results
sudo updatedb && locate nginx.conf # indexed search
fd -e yml # fast interactive search
grep -rl 'text' /path # files containing text
command -v nginx # where a command lives
