TechEarl

find Command Cheat Sheet: Search, Filter, and -exec Examples

A scannable find reference: search by name, size, time, type, perms; safe pipelines with -print0 and xargs -0; -exec and -execdir; plus the macOS BSD vs GNU find divergences and Windows PowerShell equivalents.

Ishan Karunaratne⏱️ 15 min readUpdated
Share thisCopied
find command cheat sheet: search by name, type, size, time, perms; -exec, -execdir, -delete, -print0 with xargs -0; BSD vs GNU find differences on macOS; Get-ChildItem equivalents on Windows.

find walks a directory tree and matches files against expressions: name, type, size, time, owner, permission, or anything you can compose. The expression language is its own micro-DSL with order-sensitive operators, NUL-safe output via -print0, and a handful of macOS BSD versus GNU divergences that catch out scripts shipped between platforms. This page is the reference I keep open while writing batch-rename, cleanup, and pre-deploy scripts.

What does the find command do?

find recursively searches a directory tree and prints the paths of files (and directories) that match an expression. It is the canonical tool for "find every file under X that matches Y" on Unix systems. Common uses: locate files by name (-name, -iname), filter by type (-type f, -type d, -type l), filter by size (-size +100M) or modification time (-mtime -7), then act on the results with -exec, -execdir, -delete, or by piping -print0 into xargs -0. The expression is parsed left to right; tests like -type f filter, while actions like -print or -delete operate on the matches. macOS ships BSD find, which differs from GNU find in regex defaults, the absence of -regextype, and slightly different -mtime rounding. On Windows, PowerShell's Get-ChildItem covers most of the same ground with different syntax.

Time and size reference

Keep these two tables in view while you set the inputs below. find measures time in days (-mtime) or minutes (-mmin), and size with a one-letter unit suffix.

Time filters:

WantExpressionNotes
Last N minutes-mmin -NMinute resolution
Last hour-mmin -60
Last 24 hours-mtime -1Same as -mtime 0
Last N days-mtime -N- means "less than N ago"
Older than N days-mtime +N+ means "more than N ago"
Exactly N days old-mtime NThe 24-hour slice N days back (rarely what you want)
Newer than a file-newer FILEExact-timestamp comparison

Size units (-size suffix):

SuffixUnitExampleBytes
cbytes-size +500c500
kkibibytes-size +10k10,240
Mmebibytes-size +100M104,857,600
Ggibibytes-size +1G1,073,741,824

+ before the value means "larger than", - means "smaller than", no sign means "exactly" (rounded up to the next whole unit).

Try it with your own values

Set your OS, search path, name pattern, time window, and size threshold. Every command below rewrites itself with your values. The time dropdown shows the find expression each option maps to.

Find by task (jump table)

Skim by intent and jump straight to the section. Every command updates with the values you set above.

I want to...Section
Find files by name (case-sensitive or not)Find files by name
Find files by extension (one or many)Find files by extension
Find only files, only directories, or only symlinksFind files by type
Find empty directories or empty filesFind empty directories
Find files larger or smaller than a thresholdFind files by size + deep dive
Find the largest files on diskFind largest files on disk
Find files modified in the last N daysFind files modified in the last N days + deep dive
Find stale files not modified in N daysFind files not modified since
Find files owned by a user or with specific permissionsFind files by owner or permission + deep dive
Exclude a directory from the searchExclude a directory with -prune
Match files with a regular expressionfind -regex vs -name
Run a command on every matched fileRun a command on every match + -exec vs xargs
Run find operations in parallelfind with xargs -P
Safely pipe find output into another commandPipe find safely into another command
Find files containing a specific string of textFind files containing text (find + grep)
Delete files matched by find safelyFind and delete files
Archive matched files into a tarballArchive find results with tar
rsync only the files find selectedrsync selective with find
Handle the macOS BSD vs GNU find differencesmacOS BSD vs GNU find + deep dive
Decide between find and locatefind vs locate vs mlocate
Avoid the common find footgunsCommon find gotchas

Find files by name

Match every file whose name matches a pattern. This is what 80% of find invocations do.

bash· Linux (GNU)
find :search_path -name ':pattern'

Case-insensitive match (matches .log, .LOG, .Log all the same):

bash· Linux (GNU)
find :search_path -iname ':pattern'

PowerShell's -Filter is case-insensitive by default, matching Windows' filesystem behavior. On Linux/macOS, -name is case-sensitive (-iname is the insensitive variant).

Limit how deep find recurses (avoid running away into a huge tree):

bash· Linux (GNU)
find :search_path -maxdepth 2 -name ':pattern'

-maxdepth N is a GNU extension that BSD find also supports, despite being non-POSIX. Both macOS and Linux accept it.

Find files by type

TestMatches
-type fRegular files
-type dDirectories
-type lSymbolic links
-type sSockets
-type pNamed pipes (FIFOs)
-type bBlock devices
-type cCharacter devices
bash· Linux (GNU)
find :search_path -type f -name ':pattern'

Find empty files or empty directories:

bash· Linux (GNU)
find :search_path -type f -empty

Find files by size

Set the Size value and Size unit in the panel above (the unit table is in Time and size reference). + means larger than, - means smaller than.

bash· Linux (GNU)
find :search_path -type f -size +:{size_value}:{size_unit}

For "smaller than", flip the + to -: find :search_path -type f -size -:{size_value}:{size_unit}.

Find the biggest log files on disk, sorted largest first:

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -printf '%s %p\n' | sort -rn | head -20

-printf is GNU only. On BSD use -exec stat with the BSD stat format flags. That difference is one of the most common scripts-fail-on-macOS surprises.

Find files modified in the last N days

Pick a window from the Modified within dropdown above. The dropdown shows the find expression each option maps to (the time reference table explains the syntax).

bash· Linux (GNU)
find :search_path -type f :time_filter

To find files older than a window and delete them (typical cleanup script), flip the leading - in the expression to +. The full -mtime and -mmin reference, including the off-by-one rounding rule, is in Find files modified in the last 7 days:

bash· Linux (GNU)
find :search_path -type f -mtime +30 -delete

Subtle macOS difference: BSD find -mtime -N rounds the time differently than GNU. GNU rounds the file's age down to 24-hour units; BSD treats fractional days differently in some edge cases. For scripts that need exact behavior, use -newerXY (BSD) or -mmin (both) with explicit minutes.

Find files by owner or permission

bash· Linux (GNU)
find :search_path -type f -perm 0644

Find anything world-writable (security hygiene check):

bash· Linux (GNU)
find :search_path -xdev -type f -perm -o+w 2>/dev/null

Find files owned by a specific user:

bash· Linux (GNU)
find :search_path -user www-data -type f

Run a command on every match

-exec runs a command for each match. The {} placeholder is replaced with the path; the command must end with \; (escaped semicolon).

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -exec grep -l 'ERROR' {} \;

Replace \; with + to batch arguments into fewer command invocations (much faster on large trees):

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -exec grep -l 'ERROR' {} +

-execdir runs the command in the directory of the matched file. Safer when paths have spaces or weird characters because the {} is just the basename:

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -execdir rm {} \;

-delete is the shortest form. Use with extreme care: there is no confirmation, no dry run, and order matters (always test with -print first):

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -delete

A safe pattern for any destructive find: write it with -print first, eyeball the list, then change -print to -delete.

Pipe find safely into another command

find ... | xargs command looks innocent but breaks the moment a filename contains a space, newline, or quote. The fix is NUL-delimited output: -print0 from find, -0 on xargs.

bash· Linux (GNU)
find :search_path -type f -name ':pattern' -print0 | xargs -0 gzip

Why this matters: filenames on Unix can contain literally any byte except / and NUL. -print separates with newlines, but newlines are valid in filenames. The only safe separator is NUL (\0).

This pattern is the safe equivalent of find ... -exec cmd {} +, useful when you need shell features (piping, redirection) that -exec cannot give you. It pairs with the grep cheat sheet for the most common find-then-grep pipelines.

macOS BSD vs GNU find

The divergences that matter, in order of how often they bite.

FeatureGNU findBSD find (macOS default)
-regextypeSupported (-regextype posix-extended, etc.)Not supported
-regex default flavorPOSIX BRE (Emacs flavor)Basic POSIX
-printfSupported (rich format strings)Not supported; use -exec stat
-mtime fractional roundingFloors to 24h unitsDifferent rounding edge case
-iregexSupportedSupported, but different default flavor
-not operatorSupportedSupported
-delete orderSameSame
GNU long options (--help, etc.)SupportedNot supported on most options

The single most common cross-platform script failure is -printf on macOS. There is no clean BSD equivalent; either guard with if command -v gfind, or install GNU find via Homebrew (brew install findutils) and use gfind explicitly.

bash
brew install findutils
alias find='gfind'
alias xargs='gxargs'

If your shell scripts need to run on both Linux and macOS, stick to the POSIX-ish intersection: -name, -iname, -type, -size, -mtime, -mmin, -perm, -newer, -print, -print0, -exec, -execdir, -delete. Everything outside that set risks BSD/GNU drift.

Common find gotchas

1. Forgetting to quote the pattern. find . -name *.log lets the shell expand *.log first. If a matching file exists in the current directory, that gets passed as the pattern; if none does, you get a confusing "paths must precede expression" error. Always quote: find . -name '*.log'.

2. -print is unsafe in pipelines. Filenames with spaces or newlines break xargs by default. Use -print0 | xargs -0 for any pipeline that handles arbitrary paths.

3. The -exec ... {} \; semicolon is shell metacharacter. Always escape with backslash or quote: \; or ';'. Without it, the shell ends the find command at the semicolon.

4. -exec ... {} + versus -exec ... {} \;. The + form batches arguments into a single command invocation (fast). The \; form forks once per match (slow on large trees). Use + unless the command can only accept one path at a time.

5. -delete happens depth-first but order can surprise you. If you want to delete files and then empty directories, find . -depth -type d -empty -delete after the file delete pass. The -depth flag forces children before parents.

6. -maxdepth and -mindepth must come BEFORE other tests. Putting them after -name or -type is technically legal but evaluated left to right, which can produce surprising results. Place depth restrictions first.

7. -name matches the basename only, not the path. find . -name 'src/*.ts' will never match because the slash makes it a path, not a basename. Use -path '*/src/*.ts' instead.

8. find / with no filters scans the whole filesystem. Including network mounts, /proc, /sys. Always scope with a real start path or add -xdev to stay on one filesystem.

What to do next

Deep dives by task (each one is the dedicated reference for a specific find use case):

Related cheat sheets:

FAQ

TagsfindCLILinuxmacOSBSDPowerShellShell Scriptingxargs

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts

Practical LFImap reference by task: targeting, traversal, PHP wrappers, command injection, RFI, cookies, proxying, output. Real upstream flags.

LFImap Cheat Sheet: Every Flag I Actually Use

A field-tested LFImap reference: target selection, traversal wordlists, PHP wrappers (filter/input/data/expect/file), command injection, RFI, log/proxy/cookie shaping, second-order requests, and the `PWN` placeholder. Grounded in the real argparse surface.

Practical SSRFmap reference: request capture, every module, per-module options, handler setup, AWS / GCP / Azure metadata recipes, custom modules.

SSRFmap Cheat Sheet: Every Module and Flag I Actually Use

A field-tested SSRFmap reference: target capture, the real module list (readfiles, portscan, redis, fastcgi, mysql, smtp, axfr, aws, gce, alibaba, digitalocean, github, zabbix, postgres, docker, socksproxy, smbhash, tomcat, memcache, networkscan, custom), handler setup, cloud metadata workflows, and where Burp Repeater is still the better tool.