314 lines
10 KiB
Markdown
314 lines
10 KiB
Markdown
# `find` — quick reference & practical documentation
|
||
|
||
This is a compact, practical reference for the Unix/Linux `find` command aimed at DevOps engineers and system administrators. It covers common options, tests, actions, operators, examples (safe and practical) and performance tips.
|
||
|
||
---
|
||
|
||
# Synopsis
|
||
|
||
```
|
||
find [path...] [expression]
|
||
```
|
||
|
||
If no `path` is given, `find` searches the current directory (`.`). `expression` is evaluated left-to-right and can contain tests, actions and operators.
|
||
|
||
---
|
||
|
||
# Basic behaviour
|
||
|
||
* `find` walks directory trees recursively by default.
|
||
* An *expression* evaluates to true/false; when true, the default action `-print` (or whatever action is specified) is executed.
|
||
* You can control traversal order with `-depth` and `-maxdepth`/`-mindepth`.
|
||
* `-prune` prevents descending into directories.
|
||
|
||
---
|
||
|
||
# Common options and switches
|
||
|
||
* `-H` : follow symbolic links specified on the command line.
|
||
* `-L` : follow all symbolic links.
|
||
* `-P` : never follow symbolic links (default).
|
||
* `-xdev` : don't descend into directories on other filesystems (useful when walking `/`).
|
||
* `-mindepth N` : do not apply tests/actions to files at depth < N.
|
||
* `-maxdepth N` : do not descend past depth N.
|
||
* `-depth` : process directory contents before the directory itself (useful for safe deletes).
|
||
* `-mount` : same as `-xdev` (GNU find).
|
||
|
||
---
|
||
|
||
# Common tests (predicates)
|
||
|
||
* `-name pattern` : match basename with shell globbing (case-sensitive). Pattern uses shell wildcards (`*`, `?`, `[]`).
|
||
* `-iname pattern` : case-insensitive `-name`.
|
||
* `-path pattern` : match the whole path (slash-separated) with shell globbing.
|
||
* `-ipath pattern` : case-insensitive `-path`.
|
||
* `-regex pattern` : match full path with regular expression (syntax differs by `find` implementation; GNU `find` defaults to Emacs regex).
|
||
* `-type c` : file type; `f` = regular file, `d` = directory, `l` = symlink, `c` = character device, `b` = block device, `p` = FIFO, `s` = socket.
|
||
* `-perm mode` : file permission test. Modes:
|
||
|
||
* octal: `-perm 0644` (exact)
|
||
* symbolic: `-perm -u=w` (any of the bits), `-perm /u=w` (any), `-perm -0644` (all bits set)
|
||
* GNU `find` also accepts `/mode` to mean "any of the bits".
|
||
* `-user name` / `-uid uid` : owner.
|
||
* `-group name` / `-gid gid` : group.
|
||
* `-size n[cwbk]` : file size; default 512-byte blocks for some `find` versions; most use 1K blocks for `k`. GNU `find` suffixes:
|
||
|
||
* `c` = bytes
|
||
* `k` = kilobytes
|
||
* `M` = megabytes
|
||
* `G` = gigabytes
|
||
* `+n` = greater than n, `-n` = less than n, `n` = exactly n
|
||
* `-mtime n` / `-atime n` / `-ctime n` : modified / accessed / changed time in 24-hour periods; `-mtime +7` older than 7 days, `-mtime -7` less than 7 days, `-mtime 7` exactly 7.
|
||
* `-mmin n`, `-amin n`, `-cmin n` : minutes.
|
||
* `-newer file` : newer than file (can be combined with `!` to negate).
|
||
* `-empty` : empty file or directory.
|
||
* `-links n` : number of hard links.
|
||
* `-readable` / `-writable` / `-executable` : access checks from running user perspective.
|
||
|
||
---
|
||
|
||
# Common actions
|
||
|
||
* `-print` : print path (often default; some implementations require explicit `-print` with complex expressions).
|
||
* `-print0` : print paths separated by NUL (safe with whitespace/newlines).
|
||
* `-ls` : list file using `ls -dils` style.
|
||
* `-exec command {} \;` : run `command` once per matched file. `{}` is replaced by the current path. The `\;` terminator must be escaped or quoted.
|
||
* `-exec command {} +` : optimized form; appends multiple matches to command arguments like `xargs` (more efficient).
|
||
* `-ok command {} \;` : like `-exec` but prompts the user before each execution.
|
||
* `-delete` : delete matched files/directories (be careful; obeys ordering and `-depth`).
|
||
* `-prune` : exclude directory from descent (returns true; often used with `-o`).
|
||
* `-quit` : stop after first match (very efficient when used with `-print`).
|
||
* `-mindepth N`, `-maxdepth N` already described; they behave like tests.
|
||
|
||
---
|
||
|
||
# Operator precedence (GNU `find` style)
|
||
|
||
* Parentheses `(` `)` group expressions. They must be escaped or quoted: `\( ... \)` or `'(' ... ')'`.
|
||
* `!` or `-not` : logical NOT (unary).
|
||
* `-a` or implicit concatenation : logical AND.
|
||
* `-o` or `-or` : logical OR.
|
||
* Evaluation is left-to-right; `-a` has higher precedence than `-o`. Use parentheses to be explicit.
|
||
* Common pitfall: mixing `-prune` and `-o` requires careful grouping:
|
||
Example pattern to exclude `dir`:
|
||
|
||
```
|
||
find . -path ./dir -prune -o -print
|
||
```
|
||
|
||
This means: if path is `./dir` prune it (and `-prune` returns true) otherwise (`-o`) `-print`.
|
||
|
||
---
|
||
|
||
# Typical practical examples
|
||
|
||
Search examples assume `bash` shell; escape parentheses and semicolons as needed.
|
||
|
||
1. Find files by name (case-sensitive)
|
||
|
||
```
|
||
find /var/log -type f -name "syslog*"
|
||
```
|
||
|
||
2. Case-insensitive:
|
||
|
||
```
|
||
find /home -type f -iname "*.jpg"
|
||
```
|
||
|
||
3. Find empty directories:
|
||
|
||
```
|
||
find /path -type d -empty
|
||
```
|
||
|
||
4. Delete `*.tmp` files safely (print first, then delete)
|
||
|
||
```
|
||
find /data -type f -name "*.tmp" -print
|
||
find /data -type f -name "*.tmp" -delete
|
||
```
|
||
|
||
Or safer with confirmation:
|
||
|
||
```
|
||
find /data -type f -name "*.tmp" -ok rm {} \;
|
||
```
|
||
|
||
5. Find and remove files older than 30 days (safe: use `-print` first)
|
||
|
||
```
|
||
find /var/log -type f -mtime +30 -print
|
||
find /var/log -type f -mtime +30 -exec rm -- {} +
|
||
```
|
||
|
||
Prefer `-exec ... +` over `-exec ... \;` for efficiency.
|
||
|
||
6. Remove directories older than 30 days (use `-depth` to ensure files inside are removed first)
|
||
|
||
```
|
||
find /tmp -depth -type d -mtime +30 -exec rm -rf -- {} +
|
||
```
|
||
|
||
`-delete` may also be used but beware of ordering; `-depth` ensures children processed before parent.
|
||
|
||
7. Find files with spaces and handle them safely:
|
||
|
||
```
|
||
find /srv -type f -name "*.conf" -print0 | xargs -0 grep -H "pattern"
|
||
```
|
||
|
||
8. Find files larger than 100 MiB:
|
||
|
||
```
|
||
find / -type f -size +100M -print
|
||
```
|
||
|
||
9. Find files newer than a reference file:
|
||
|
||
```
|
||
find /web -type f -newer /tmp/deploy_marker -print
|
||
```
|
||
|
||
10. Find and run a command on many files (batching):
|
||
|
||
```
|
||
find /data -type f -name "*.log" -exec gzip -- {} +
|
||
```
|
||
|
||
11. Exclude a directory (`dir_to_skip`) while listing everything else:
|
||
|
||
```
|
||
find . -path "./dir_to_skip" -prune -o -print
|
||
```
|
||
|
||
12. Only search one level (non-recursive):
|
||
|
||
```
|
||
find . -maxdepth 1 -type f -print
|
||
```
|
||
|
||
13. Find broken symlinks:
|
||
|
||
```
|
||
find / -xtype l -print
|
||
```
|
||
|
||
Note: `-xtype` tests the target type; `-L` changes behavior of `-type`.
|
||
|
||
14. Use `-printf` to customize output (GNU find):
|
||
|
||
```
|
||
find . -type f -printf "%p\t%k KB\t%TY-%Tm-%Td %TH:%TM:%TS\n"
|
||
```
|
||
|
||
`-printf` supports format sequences (`%p` path, `%s` bytes, `%k` KB blocks, `%TY` year, ...). Exact spec depends on implementation (GNU has extensive options).
|
||
|
||
15. Stop at first match (fast):
|
||
|
||
```
|
||
find /usr -type f -name "passwd" -print -quit
|
||
```
|
||
|
||
16. Find files modified within last 15 minutes:
|
||
|
||
```
|
||
find /var -type f -mmin -15
|
||
```
|
||
|
||
---
|
||
|
||
# Safe deletion checklist
|
||
|
||
* Always `-print` or `-ls` first to verify matches.
|
||
* Prefer `-exec rm -- {} +` or use `-delete` with `-depth` if required.
|
||
* Watch out for `-maxdepth`/`-mindepth` to avoid accidental top-level deletions.
|
||
* Use `-print0` + `xargs -0` when passing to other utilities.
|
||
* Consider using `-ok` for destructive changes in interactive contexts.
|
||
|
||
---
|
||
|
||
# Performance & scalability tips
|
||
|
||
* Limit scope: pass explicit starting paths instead of `.` or `/` when possible.
|
||
* Use `-maxdepth` and `-mindepth` to reduce traversal.
|
||
* Use `-xdev` to avoid crossing filesystem boundaries (speeds up root scans).
|
||
* Combine fast tests early (e.g., `-type f -name "*.log"` rather than `-name` alone) because find evaluates left-to-right; short-circuiting happens when possible.
|
||
* Prefer `-exec ... +` to `-exec ... \;` to reduce process spawn overhead.
|
||
* Use `-prune` to skip large tree branches that aren’t needed.
|
||
* On very large trees consider tools optimized for indexing (e.g., `locate` / `mlocate`, `fd`, or ripgrep for content search). `find` is always consistent but traverses the disk each run.
|
||
|
||
---
|
||
|
||
# Portability notes
|
||
|
||
* Behavior, available tests/actions and argument formats vary slightly between implementations (GNU `find` vs BSD `find` vs BusyBox). Examples above assume GNU `find` when using `-printf`, `-delete`, `-quit` or suffixes like `M`/`G` for sizes.
|
||
* When writing scripts for mixed environments, try to stick to portable tests: `-name`, `-type`, `-mtime`, `-print`, `-exec ... \;`. Check `/usr/bin/find --version` or `man find` on target hosts.
|
||
|
||
---
|
||
|
||
# Gotchas & common pitfalls
|
||
|
||
* Shell globbing vs `find` globbing: `-name "*.txt"` is matched by `find` and must be quoted to prevent shell expansion.
|
||
* `-perm` exact vs any-bit semantics differ across versions; test on target system.
|
||
* `-regex` matches the whole path, not just basename; regex dialect differs (use `-regextype posix-extended` on GNU `find` to set type).
|
||
* `-delete` will fail if used before tests that select children (order matters). Use `-depth` or test ordering appropriately.
|
||
* Beware of running `find` as root with `-exec rm -rf {}` — very dangerous if expression is wrong. Always verify output.
|
||
* `-maxdepth`/`-mindepth` may not be supported in very old `find` implementations.
|
||
|
||
---
|
||
|
||
# Exit status
|
||
|
||
* `0` : at least one match and command completed successfully.
|
||
* `1` : no matches were found (behavior can vary).
|
||
* `>1` : an error occurred (e.g., permission errors or malformed expression).
|
||
Note: exact meanings can depend on implementation.
|
||
|
||
---
|
||
|
||
# Useful combinations
|
||
|
||
* Find files and preserve relative paths for tar:
|
||
|
||
```
|
||
cd /path && find . -type f -name "*.conf" -print0 | tar --null -T - -czf confs.tar.gz
|
||
```
|
||
|
||
* Find and change ownership or permissions:
|
||
|
||
```
|
||
find /srv/www -type f -name "*.php" -exec chown www-data:www-data {} +
|
||
find /srv/www -type d -exec chmod 755 {} +
|
||
```
|
||
|
||
* Search content in matched files:
|
||
|
||
```
|
||
find /app -type f -name "*.py" -print0 | xargs -0 grep -n "TODO"
|
||
```
|
||
|
||
---
|
||
|
||
# Related commands/tools
|
||
|
||
* `locate` / `mlocate` : fast filename lookup (uses database).
|
||
* `fd` : simpler, faster user-friendly alternative to `find` (not always installed).
|
||
* `xargs` : pass batches of arguments to commands (careful with spaces; use `-print0` + `xargs -0`).
|
||
* `stat` : detailed file metadata for a single file.
|
||
* `rm`, `tar`, `gzip`, `chown`, `chmod`, `rsync` : commonly used alongside `find`.
|
||
|
||
---
|
||
|
||
# Quick cheatsheet (most-used patterns)
|
||
|
||
* Find files by name: `find /path -type f -name "pattern"`
|
||
* Case-insensitive: `-iname`
|
||
* Size bigger than 100M: `-size +100M`
|
||
* Modified > 7 days: `-mtime +7`
|
||
* Delete old files: `find /dir -type f -mtime +30 -exec rm -- {} +`
|
||
* Print NUL-separated: `-print0`
|
||
* Safe prune: `find . -path ./skip -prune -o -print`
|
||
* Batch exec: `-exec cmd {} +`
|
||
* Stop on first: `-print -quit`
|