How to Find Files and Directories in Linux

How to Find Files and Directories in Linux

Reading time1 min
#linux#find#locate#command-line#sysadmin

How to Find Files and Directories in Linux

Linux has several search tools, and each answers a different question:

  • find walks the filesystem live and filters by anything: name, type, size, age, owner, permissions.
  • locate searches a prebuilt index. It is instant, but only as fresh as the last index update.
  • fd is a faster, friendlier find for interactive use.
  • grep -r and rg search inside files, not file names.
  • command -v and type find programs in your PATH.

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 -0 handles file names with spaces and newlines safely.
  • -ok works like -exec but 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