💻 find Quick Reference

find cheat sheet / survival guide

Video: find (Command Line Tips video)
Prefer to watch? This short video runs this page's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
find [starting-point...] [expression]
find . -name "*.log"

find walks a directory tree and tests every file against your expression.
With no starting point it uses the current directory (GNU find).
Quote patterns so the shell does not expand them before find sees them.

Anatomy of an expression

Tests      -name, -type, -size, -mtime ...   (what to match)
Actions    -print, -delete, -exec ...        (what to do; -print is the default)
Operators  -and (default)  -or  -not  ( )    (how to combine)
Options    -maxdepth, -mindepth, -depth      (put these before tests)

Match by name

find . -name "report.txt"                  # exact name (case-sensitive)
find . -iname "*.jpg"                      # case-insensitive
find . -name "*.log" -o -name "*.tmp"      # either pattern (-o = or)
find . -name "*.py" ! -name "test_*"       # not matching
find . -path "*/src/*.js"                  # match against the full path
find . -regex '.*\.\(jpg\|png\)$'          # regex on the full path
find . -regextype posix-extended -regex '.*\.(jpg|png)$'   # GNU: extended regex

Match by type

-type f    Regular file
-type d    Directory
-type l    Symbolic link
-type b/c  Block / character device
-type s/p  Socket / named pipe
find . -type f -name "*.sh"
find /var -type d -name "cache"
find . -xtype l                            # broken symlinks (GNU)

Match by size

find . -size +100M                         # bigger than 100 MB
find . -size -1k                           # smaller than 1 KB
find . -size 0                             # exactly zero bytes
find . -type f -empty                      # empty files (also -empty for dirs)
find / -xdev -type f -size +500M 2>/dev/null   # big files, this filesystem only
Units: c bytes, k KiB, M MiB, G GiB. Plain numbers are 512-byte blocks.
Sizes round UP, so -size -1M means "empty" (0 units); use -size -1024k instead.

Match by time

find . -mtime -1                           # modified in the last 24 hours
find . -mtime +30                          # modified more than 30 days ago
find . -mtime 0                            # modified in the last 24h (day 0)
find . -mmin -15                           # modified in the last 15 minutes
find . -atime +90                          # accessed more than 90 days ago
find . -ctime -7                           # metadata changed in the last 7 days
find . -newer reference.txt                # modified more recently than a file
find . -newermt "2026-01-01"               # modified after a date (GNU)
find . -newermt "2026-01-01" ! -newermt "2026-02-01"   # within a date range
-mtime n counts whole 24-hour periods: +n means older than n+1 days.

Match by owner and permissions

find . -user alice                         # owned by a user
find . -group devs                         # owned by a group
find . -nouser -o -nogroup                 # orphaned files
find . -perm 644                           # exactly mode 644
find . -perm -u+x                          # owner can execute (all listed bits set)
find . -perm /o+w                          # world-writable (any listed bit set)
find / -perm -4000 -type f 2>/dev/null     # setuid files
find . -readable -writable -executable     # what the current user can do (GNU)

Depth and scope

find . -maxdepth 1 -type f                 # this directory only
find . -mindepth 2 -maxdepth 3             # skip top level, stop at depth 3
find . -xdev                               # stay on one filesystem
find . -prune                              # do not descend (see below)
find . -path ./node_modules -prune -o -type f -print   # skip a directory
find . \( -name .git -o -name node_modules \) -prune -o -type f -print

Actions

-print      Print the path (default). Newline-separated.
-print0     Print NUL-separated (safe for odd filenames, pair with xargs -0)
-ls         Long listing, like ls -dils
-delete     Delete matches (test with -print first!)
-exec cmd {} \;   Run cmd once per file
-exec cmd {} +    Run cmd with many files at once (faster)
-ok cmd {} \;     Like -exec but asks for confirmation first
-printf FMT       Custom output (GNU)   e.g. -printf '%p %s\n'
-quit             Stop after the first match (GNU)

find . -name "*.tmp" -print                # see what would go...
find . -name "*.tmp" -delete               # ...then delete it
find . -type f -name "*.sh" -exec chmod +x {} +
find . -name "*.log" -exec gzip {} \;
find . -name "*.jpg" -exec cp {} /backup/ \;
find . -type f -exec grep -l "TODO" {} +   # files containing a string
find . -type f -exec sha256sum {} +        # checksum everything
find . -name "*.bak" -ok rm {} \;          # confirm each deletion
find . -type f -printf '%s %p\n' | sort -rn | head    # biggest files first
find . -type f -printf '%TY-%Tm-%Td %p\n'  # modification date and path
find . -name "core" -print -quit           # first match only

Combining tests

find . -type f -name "*.py" -size +10k     # AND is the default
find . -name "*.jpg" -o -name "*.png"      # OR
find . ! -name "*.txt"                     # NOT (also -not)
find . -type f \( -name "*.jpg" -o -name "*.png" \) -size +1M   # group with \( \)
find . -type f -mtime +30 -name "*.log" -delete
Parentheses must be escaped or quoted: \( ... \). Put a space inside each.

Safe filenames (spaces, newlines)

find . -name "*.txt" -print0 | xargs -0 wc -l
find . -type f -print0 | xargs -0 -n1 -P4 gzip       # 4 in parallel
find . -type f -print0 | while IFS= read -r -d '' f; do echo "$f"; done
find . -name "*.mp3" -exec mv {} ~/Music/ \;         # -exec handles spaces safely

Pipelines

find . -type f | wc -l                                # count files
find . -type d | wc -l                                # count directories
find . -type f -name "*.py" | xargs wc -l | tail -1   # total lines of Python
find . -type f -printf '%f\n' | sort | uniq -d        # duplicate file names
find . -type f -exec md5sum {} + | sort | uniq -w32 -d   # duplicate contents
find . -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn   # count by extension

Practical one-liners

# Files changed in the last hour
find . -type f -mmin -60

# Delete empty directories
find . -type d -empty -delete

# Delete files older than 30 days
find /var/backups -type f -mtime +30 -delete

# Find the 10 largest files under /var
find /var -xdev -type f -printf '%s %p\n' 2>/dev/null | sort -rn | head -10

# Find and replace text across files
find . -type f -name "*.conf" -exec sed -i 's/old/new/g' {} +

# Fix permissions: 755 for dirs, 644 for files
find . -type d -exec chmod 755 {} +
find . -type f -exec chmod 644 {} +

# World-writable files (security check)
find / -xdev -type f -perm -0002 2>/dev/null

# Find files by inode number, then remove a file with a strange name
find . -inum 123456 -exec rm {} \;

# List directories only, one level deep
find . -mindepth 1 -maxdepth 1 -type d

# Find recent files and sort by newest
find . -type f -printf '%T@ %p\n' | sort -rn | head

# Silence "Permission denied" noise
find / -name "nginx.conf" 2>/dev/null

Exit codes

    0 - finished without errors

    1 - some directories could not be read (for example, permission denied)

Tips

    Options like -maxdepth and -xdev should come before tests; GNU find warns if they do not.

    Test with -print (or no action) before using -delete or -exec rm. Delete is unforgiving.

    -delete implies -depth, so it works from the bottom up. Do not put it before your tests.

    Quote patterns: -name "*.txt", not -name *.txt, or the shell may expand it first.

    -name matches only the file name. Use -path or -regex to match the whole path.

    Use -exec ... {} + when the command accepts many files; it runs far fewer processes than \;.

    Prefer -print0 with xargs -0 whenever filenames might contain spaces or newlines.

    On macOS/BSD find, a starting point is required and options such as -printf, -regextype and -quit are GNU-only.

    For very fast name lookups across the whole system, consider locate (uses a prebuilt index) or fd.