Skip to content
VLSI Mentor

Linux & Bash · Part I · Module 14 · Finding Files and Searching Their Contents

find: Locating Files by Name, Type, Time and Size

Five chapters have asked which files contain something. This one asks which files exist — matched by name, type, age, size or depth, and combined with the ordinary logical operators. The syntax is unusual and the reason to learn it is that nothing else can express the question.

Intermediate19 min readLinuxPart IFundamentalsSearchingfind

Part I · Module 14 · Chapter 14.6 · find: Locating Files by Name, Type, Time and Size

A different question from the last five chapters, and a tool shaped around it.

1. The Shape of the Command

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -name '*.log'
./build/gen.log
./new.log
./old.log
./sub/deep.log

find WHERE CONDITIONS — a starting directory, then the conditions a file must satisfy. Every matching path is printed, recursively, with no depth limit by default.

The order is fixed and catches people: the path comes first. find -name '*.log' . is wrong.

Conditions combine by simply writing them one after another:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -type f -name '*.log' -size +1k

Adjacent conditions mean "and". A file must be a regular file, and be named *.log, and be over a kilobyte.

2. By Name

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log'           # case-sensitive
find . -iname '*.LOG'          # case-insensitive
find . -name 'run_*'           # prefix
find . -name '*_tb.sv'         # suffix

The pattern is a glob (Chapter 3.5), not a regular expression — the same language --include uses in Chapter 14.4, and a different one from Chapter 14.2. * means any run of characters here.

-name matches the base name only. To match against the whole path, use -path:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -path '*/rtl/*.v'       # .v files under any rtl directory
find . -path '*/build/*'       # anything under a build directory

And -regex matches the whole path with a regular expression, in the basic dialect by default (Chapter 14.3) — find -E on BSD and -regextype on GNU switch dialects. It is rarely worth it; -path covers most of what people reach for it for.

3. By Type

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -type f | wc -l
6
$ find . -type d
.
./build
./sub
TypeMatches
fA regular file
dA directory
lA symbolic link
pA named pipe
sA socket

-type f belongs in almost every find you write. Without it, a directory named logs.log matches -name '*.log', and anything you then do to the results treats a directory as a file.

-type l is the one for auditing links (Chapter 6.3):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -type l                        # every symbolic link
find . -type l ! -exec test -e {} \; -print    # the dangling ones

4. By Time

The most useful conditions and the most confusing arithmetic.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -name '*.log' -mtime -1
./build/gen.log
./new.log
./sub/deep.log

$ find . -name '*.log' -mtime +30
./old.log
WrittenMeans
-mtime -1Modified less than 1 day ago
-mtime +30Modified more than 30 days ago
-mtime 7Modified exactly 7 days ago — that 24-hour window

The three timestamps (Chapter 2.5 met them):

ConditionTimestamp
-mtime / -mminModified — contents changed
-atime / -aminAccessed — contents read
-ctime / -cminChanged — metadata changed, e.g. permissions

-mtime is what you want nearly always. -atime is unreliable because many systems mount filesystems with access-time updates disabled for performance.

5. By Size

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -size +100k -type f
./big.bin

The suffix is the unit, and the sign is the comparison:

SuffixUnit
cBytes
kKilobytes
MMegabytes
GGigabytes
(none)512-byte blocks
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -size +100M              # larger than 100 megabytes
find . -size -1k                # smaller than 1 kilobyte
find . -size 0                  # exactly zero blocks — but see below
find . -empty                   # empty files or directories

6. Combining Conditions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -maxdepth 1 -type f ! -name '*.log'
./big.bin
./recent.txt
OperatorMeans
(adjacent)And — -a is the explicit form
-oOr
!Not
\( \)Grouping
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -maxdepth 1 \( -name '*.txt' -o -name '*.bin' \)
./big.bin
./recent.txt

The parentheses need escaping because they are shell metacharacters (Chapter 9.3) — \( and \), or '(' and ')'. Without them the operators bind in a way you did not intend, because -o has lower precedence than the implicit and.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log' -o -name '*.tmp' -delete       # DANGEROUS — see below
find . \( -name '*.log' -o -name '*.tmp' \) -delete # correct

7. Controlling the Walk

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -maxdepth 1 -name '*.log'
./new.log
./old.log

-maxdepth N limits how deep it descends and -mindepth N skips the shallow levels. -maxdepth 1 means "this directory only", which is the common one.

Both must appear before other conditions to work reliably — they are options rather than tests, and GNU find warns when they appear late.

Pruning

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ find . -name build -prune -o -name '*.log' -print
./new.log
./old.log
./sub/deep.log

-prune tells find not to descend into a matching directory. The build tree is skipped entirely rather than searched and filtered.

The shape is always the same and worth memorising rather than deriving:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name DIR -prune -o CONDITIONS -print

Three things make it work:

  • -prune succeeds, so without the -o everything would stop there.
  • -o means the rest only applies to things that were not pruned.
  • -print is required. Normally find prints by default, but adding any action — and -prune is an action — switches the default off for the whole expression.

Several at once:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . \( -name .git -o -name build -o -name work \) -prune -o -name '*.v' -print

8. What It Prints

By default find prints each matching path, one per line. Three other actions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log' -print           # the default, stated explicitly
find . -name '*.log' -print0          # separated by null bytes — Chapter 14.7
find . -name '*.log' -ls              # a long listing, like ls -l
find . -name '*.log' -printf '%s\t%p\n'   # chosen fields — GNU only

-printf is GNU-only and absent from BSD find. It is genuinely useful when available — -printf '%s\t%p\n' gives size and path, tab-separated, which is Chapter 13.1's machine-readable format rather than something to parse.

The portable equivalent is -exec stat, which Chapter 14.7 covers.

9. In Practice

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# the one you will type most
find . -type f -name '*.sv'

# this directory only
find . -maxdepth 1 -type f -name '*.log'

# recently changed source, skipping the build tree
find . -name build -prune -o -type f -name '*.v' -mmin -60 -print

# large files eating the disk
find /scratch -type f -size +100M -ls 2>/dev/null

# everything a run produced, by comparison with a marker
touch /tmp/run.stamp
./run_sim.sh
find . -newer /tmp/run.stamp -type f

# empty files, which usually mean a failed step
find runs/ -type f -empty

# dangling symbolic links
find . -type l ! -exec test -e {} \; -print

# every directory containing a filelist
find . -name 'filelist.f' -exec dirname {} \;

The marker-file pattern in the middle is the one worth adopting. touch a stamp, do the work, find -newer answers "what did that just produce?" exactly, with none of -mtime's arithmetic.

Practice

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
mkdir -p /tmp/fd/{sub,build} && cd /tmp/fd
touch -t 202501010000 old.log
touch new.log recent.txt sub/deep.log build/gen.log
dd if=/dev/zero of=big.bin bs=1k count=200 2>/dev/null
ln -s nowhere dangling.link
touch empty.txt

1 — By name and type.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log'
find . -type f | wc -l
find . -type d
find . -type l

2 — Hit the quoting trap.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -maxdepth 1 -name '*.log'
find . -maxdepth 1 -name *.log

Two results versus one. Explain it before reading §2 again.

3 — Limit the depth.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log'
find . -maxdepth 1 -name '*.log'

4 — By size, and meet the rounding.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -size +100k -type f
find . -size -1k -type f
find . -size -1024c -type f
find . -type f -empty

The second returns nothing. The third and fourth find the small files.

5 — By time.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log' -mtime -1
find . -name '*.log' -mtime +30
find . -mmin -5 -type f
touch /tmp/fd/marker; sleep 1; touch brandnew.txt
find . -newer /tmp/fd/marker -type f

6 — Combine with not and or.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -maxdepth 1 -type f ! -name '*.log'
find . -maxdepth 1 \( -name '*.txt' -o -name '*.bin' \)

7 — Prune rather than filter.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -name '*.log'
find . -name build -prune -o -name '*.log' -print
find . -name '*.log' ! -path '*/build/*'

The last two agree; only one avoids walking build.

8 — Watch the -o precedence trap, safely.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -maxdepth 1 -name '*.log' -o -name '*.txt' -print
find . -maxdepth 1 \( -name '*.log' -o -name '*.txt' \) -print

The first prints only the .txt files with their paths — the .log branch has no action attached.

9 — Find the dangling link.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
find . -type l ! -exec test -e {} \; -print

10 — Clean up. cd /tmp && rm -rf /tmp/fd

Check Your Understanding

1. Why must -name '*.log' be quoted, and why does the failure depend on where you are standing?

2. What does -mtime +1 actually match, and what is the better way to say "in the last day"?

3. find . -size -1k returns nothing on a directory full of small files. Why?

4. find . -name '*.log' -o -name '*.tmp' -delete — what does it delete?

5. What is the difference between -prune and ! -path '*/build/*'?

6. Why does -prune need an explicit -print on the other branch?

10. Remember

  • find WHERE CONDITIONS — the path comes first, and adjacent conditions mean and.
  • It asks the filesystem directly, which is why it can express age, size and type where parsing ls cannot.
  • Quote every -name pattern. Unquoted, the shell expands it first and the bug depends on where you were standing.
  • -name matches the base name; -path matches the whole path.
  • -type f belongs in almost every find you write.
  • -mtime counts whole days rounded down, so -mtime +1 skips the 24-to-48-hour band. Use -mmin for short spans.
  • -newer marker is the exact way to ask "what changed since?" Touch a stamp, do the work, compare.
  • -size needs a suffix — a bare number means 512-byte blocks — and rounds up, so -size -1k matches nothing.
  • -empty is the right test for empty, not -size 0.
  • ! negates, -o is or, and \( \) groups — the parentheses need escaping.
  • -o with an action is dangerous without grouping, because the implicit and binds tighter.
  • -maxdepth 1 means this directory only, and depth options must come first.
  • Prune, do not filter. -prune never enters the directory; ! -path walks it and discards.
  • Any action switches off the default print, which is why the prune idiom needs -print.
  • -printf is GNU-only.
  • The -mtime off-by-one stops being academic when find feeds a deletion — Chapter 22.4 §5 measures a purge rule that keeps the file it was written to remove.
  • A generated filelist is a find away, and -printf is a GNU extension — Chapter 26.3 measures it failing in a script while working at the prompt, because the prompt's find is bfs.

Finding the files is half of it. The next chapter runs something on them — safely, including the names with spaces in, which is where the obvious version quietly breaks.

Where this fits

Part of the Linux & Bash curriculum.