Find Command
— ny_wk

Disclosure: some links above are affiliate links — if you buy through them I may earn a small commission at no extra cost to you. Thanks for supporting the channel!
The Linux find command searches a directory tree for files and directories that match criteria you specify — name, size, modification time, owner, permissions, type — and can run any command on the matches. This guide shows the exact syntax for the most useful searches, explains what each flag does, warns about the traps that bite people, and ends with verification steps so you know your command did what you intended.
The problem the find command solves
You know a file exists somewhere on the system, but you do not know the path. Maybe it is a stray .conf file, a multi-gigabyte log eating your disk, or every .jpg created in the last day. Tools like a desktop search box are slow and incomplete on a server. The Linux find command walks the filesystem live, evaluates a logical expression against every entry, and prints (or acts on) exactly what matches.
Its companion, locate, answers a narrower question — "where is a file with this name?" — instantly, by querying a prebuilt database. The two are complementary, and the end of this guide covers when to reach for each.
How the find command is structured
Every find invocation follows the same shape:
find [options] [path...] [expression]
- path — one or more starting directories.
/means the whole filesystem;.means the current directory. If you omit the path entirely,finddefaults to the current directory. - expression — a chain of tests (
-name,-size,-mtime…), actions (-print,-exec,-delete) and operators (-and,-or,!) that are evaluated left to right.
If you give no expression, find implies -print and simply lists everything beneath the path. Two expressions placed side by side are joined with an implied logical AND, so -name '*.log' -size +1M means "name matches and size is over 1 MB".
Always quote the pattern
Wrap any pattern containing wildcards in single quotes — '*.c', not *.c. Without quotes the shell expands * against files in your current directory before find ever runs, producing confusing errors or wrong results. Quoting hands the literal pattern to find, which is what you want.
Finding files by name with the find command
The most common search is by filename. Start the search at the root and look for an exact name:
- Case-sensitive search across the whole system:
find / -name 'program.c' 2>/dev/null - Case-insensitive (matches
INDEX,Index,index):find /home/david -iname 'index*' - Search only the current directory and below (path omitted):
find -name 'met*' - Invert the match — everything not matching the pattern:
find . ! -name '*.jpg'
The 2>/dev/null piece is not part of find at all — it is a shell redirection. Stream 2 is standard error, and /dev/null discards anything sent to it. So 2>/dev/null silences the "Permission denied" noise find prints when it hits directories you cannot read. Swap in 2>errors.txt to save those messages to a file instead.
Note that -name matches only the base name (the last path component). To match against the full path, use -path or its case-insensitive sibling -ipath, and for regular-expression matching against the whole path use -regex.
Finding files by type
Restrict results to a particular kind of filesystem object with -type:
| Descriptor | Matches |
f | Regular file |
d | Directory |
l | Symbolic link |
c | Character device |
b | Block device |
p | Named pipe (FIFO) |
s | Socket |
Find every regular file ending in .conf:
find / -type f -name '*.conf'
List only directories named apple in or below the current directory:
find . -name 'apple' -type d
List every character device on the system:
find / -type c
Filtering by size
The -size test takes a number plus a unit suffix. A leading + means "larger than", a leading - means "smaller than", and a bare number means "exactly".
| Suffix | Unit |
c | Bytes |
k | Kilobytes (1024 bytes) |
M | Megabytes (1048576 bytes) |
G | Gigabytes |
b | 512-byte blocks (the default if no suffix) |
- Exactly 50 bytes:
find / -size 50c - Smaller than 5 MB:
find /mp3collection -name '*.mp3' -size -5000k - Larger than 700 MB:
find / -size +700M - Regular files over 1 GB:
find . -size +1G -type f
Watch the no-suffix trap: find / -size +10000 with no unit counts 512-byte blocks, not kilobytes. To search by kilobytes you must write the k: find / -size +10000k finds files over roughly 10 MB.
Filtering by time with the find command
Linux tracks three timestamps per file, and find can test each one:
- Access time (
-atime/-amin) — last time the file was read. - Modification time (
-mtime/-mmin) — last time the file's contents changed. - Change time (
-ctime/-cmin) — last time the inode metadata (permissions, owner, link count) changed.
The *time tests count in 24-hour periods; the *min tests count in minutes. As with size, + means "more than" and - means "less than".
- Modified in the last 10 minutes, ending in
.c:find /home/david -mmin -10 -name '*.c' - Accessed within the last 10 minutes:
find /home/david -amin -10 -name '*.c' - Modified less than 2 days ago:
find /home/david -mtime -2 -name '*.c' - Metadata changed more than 3 days ago:
find / -ctime +3 - Newer than a reference file:
find / -newer /etc/passwd
An important correction: the original phrasing "accessed in the last 10 hours" for -atime -2 is wrong. Because -atime works in whole 24-hour units, -atime -2 means "accessed less than two days ago", and the fractional remainder is discarded. Likewise, to match -atime +1 a file must have been accessed at least two full days ago. When you need minute precision, use -amin/-mmin instead.
Filtering by owner and permissions
Match files by their owning user or group:
- Owned by user
syslog:find / -user syslog - Owned by group
shadow:find / -group shadow - Owned by nobody (orphaned UID):
find / -nouser
Permission searches have three forms, and the distinction matters:
find / -perm 644— an exact match; the mode must be precisely644.find / -perm -644— files that have at least these bits set (extras allowed). A file with mode744matches.find / -perm /644— files that have any of these bits set. This/modeform replaces the old, deprecated+modesyntax.
You can also use symbolic notation: find . -perm u=rwx,g=rx,o=r is the symbolic equivalent of 754. A handy security sweep — binaries that are executable but unreadable:
find /sbin /usr/sbin -executable ! -readable
Controlling search depth
On a deep tree, an unrestricted search can return thousands of hits. Bound the recursion with depth limits:
-maxdepth N— descend at mostNlevels below each starting point.-maxdepth 0tests only the start points themselves.-mindepth N— ignore everything shallower than levelN.
Limit a name search to two levels deep:
find . -maxdepth 2 -name 'file1'
Search only a middle band of the tree:
find . -mindepth 2 -maxdepth 3 -name 'file1'
Place depth options before the tests for clarity; they are global options, so their position does not change behavior, but reading the command is easier when limits come first.
Combining tests with boolean operators
Logical operators turn find into a precise filter. AND is implied when you omit an operator; spell it out with -and for readability. Use -or for alternatives and ! (or -not) to negate.
- Name starts with
Metallicaand larger than 10 MB:find /mp3-collection -name 'Metallica*' -and -size +10000k - Larger than 10 MB but not a
Metallicafile:find /mp3-collection -size +10000k ! -name 'Metallica*' - Either condition (or):
find /mp3-collection -name 'Metallica*' -or -size +10000k - Match two extensions:
find . -name '*.jpg' -o -name '*.gif'
When you need grouping, parenthesize — but escape the parentheses so the shell does not interpret them: find . \( -name '*.jpg' -o -name '*.gif' \) -size +1M.
Acting on results: -exec, -delete, and xargs
The real power of the find command is doing something with each match, not just printing it. The -exec action runs a command per file:
find / -name 'Metallica*' -exec ls -l {} \;
Here {} is replaced by each matched path, and \; terminates the command (the backslash protects the semicolon from the shell). A few practical examples:
- Tighten permissions on every file currently at
644:find . -perm 644 -exec chmod 664 {} \; - Move every file over 1 GB into a folder:
find . -size +1G -exec mv {} ~/bigfiles \; - Ask before each action (interactive confirmation):
find . -size +1G -ok mv {} ~/bigfiles \; - Delete matched files directly:
find /tmp -name 'core' -type f -delete
For large result sets, pipe to xargs instead — it batches many filenames into far fewer command invocations, which is much faster:
find /tmp -name 'core' -type f -print0 | xargs -0 /bin/rm -f
Always pair find -print0 with xargs -0. The -print0 action separates names with a null byte instead of a newline, so filenames containing spaces or newlines are parsed correctly. There is also a built-in batched form, find ... -exec rm {} +, which appends many filenames per call like xargs does.
Safety note for -delete: because the expression is evaluated left to right, never put -delete first — find . -delete -name foo would try to remove the whole tree before the name test runs. Test with -print first, confirm the list, then swap in -delete.
find versus locate
Where find walks the live filesystem, locate queries a precomputed database — so it returns name matches almost instantly, but only as fresh as the last database update. Install and seed it like this:
- Install on Debian/Ubuntu:
sudo apt-get install mlocate - Build or refresh the index:
sudo updatedb - Search:
locate query
Useful flags: locate -b 'name' matches only the base name; locate -e 'name' returns only entries that still exist on disk; locate -S prints database statistics. The database is normally rebuilt nightly by cron, so files created today will not appear until you run updatedb manually.
| Use locate when | Use find when |
| You only need a name and want speed | You filter by size, time, owner, or permissions |
| The file is not brand new | You need up-to-the-second accuracy |
| A read-only lookup is enough | You want to run a command on each match |
Common pitfalls and how to avoid them
- Unquoted wildcards. Forgetting quotes lets the shell expand
*first. Always write-name '*.log'. - Confusing the time units.
-mtime 1is roughly "between 24 and 48 hours ago", not "today". Use-mtime 0for the last 24 hours, or-mminfor minutes. - Forgetting the size suffix. A bare number on
-sizecounts 512-byte blocks, not kilobytes. - Putting
-deletein the wrong place. It implies-depthand runs as it evaluates — always dry-run with-printfirst. - Following symlinks unintentionally. The default is
-P(never follow). Add-Lonly when you truly want to resolve links, since it can leadfindoff the tree you intended. - Permission-denied noise. Redirect with
2>/dev/nullto hide it, or2>&1 | grep -v 'Permission denied'to hide only that message while keeping other errors.
Verifying your find command worked
Before running anything destructive, confirm the match set:
- Count first. Pipe to a counter to gauge scope:
find . -name 'file1' | wc -l. - Preview, then act. Run the search with
-print(the default) and read the list before adding-execor-delete. - Inspect details. Use
-lsto see size, owner, and timestamps inline:find . -size +1G -ls. - Confirm after acting. Re-run the same test — if you deleted or moved everything, the second run should return nothing.
Key Takeaways
- The syntax is
find [path] [expression]; with no path it searches the current directory, and with no expression it lists everything. - Always single-quote wildcard patterns so the shell does not expand them before
findruns. - The
+/-prefixes on-sizeand the*timetests mean "more than" and "less than"; remember that time tests use whole-day units unless you use-amin/-mmin. - Use
-exec ... {} \;or pipe-print0toxargs -0to run commands on matches — and always dry-run before-delete. - Reach for
locatefor fast name-only lookups; reach forfindwhen you need filters, freshness, or actions.
Frequently Asked Questions
How do I find a file by name in Linux?
Use find /path -name 'filename', or -iname to ignore case. To search the whole system while hiding permission errors, run find / -name 'filename' 2>/dev/null. For an instant name-only lookup, locate filename is faster once its database is current.
What is the difference between find and locate?
find scans the live filesystem and supports rich filters (size, time, owner, permissions) plus running commands on results. locate reads a prebuilt database, so it is much faster for plain name searches but may miss files created since the last updatedb.
How do I find files larger than a certain size?
Use the -size test with a unit suffix and a + prefix for "larger than". For example, find / -size +1G -type f lists every regular file over one gigabyte. Always include the unit (k, M, G) — a bare number counts 512-byte blocks.
How do I delete files found with the find command safely?
Preview first with find . -name '*.tmp' -print, confirm the list, then re-run with -delete at the end of the expression. Never place -delete before the tests, and for large batches use find ... -print0 | xargs -0 rm for speed.
For more hands-on Linux and sysadmin walkthroughs, subscribe on YouTube @explorenystream.