Find -maxdepth and -mindepth: Count from Each Starting Point
find ./project -maxdepth 1 -print includes ./project itself. The starting point is depth 0; its immediate children are depth 1. To list only those children, use both limits:
find ./project -mindepth 1 -maxdepth 1 -print
The command lists files and directories alike, including hidden entries. Depth is always relative to the starting point: the same file can be at depth 2 in one search and depth 1 in another. The lab below lets you check those boundaries and catch a less obvious mistake: a minimum depth can stop a pruning action from running.

Start counting at the path you give find
The GNU find manual describes two different controls:
| Option | What it controls |
|---|---|
-maxdepth N |
Descend no farther than level N below each starting point. Entries at level N can still be tested and printed. |
-mindepth N |
Skip tests and actions at levels smaller than N. Continue through shallower directories to reach eligible entries. |
Use a non-negative whole number for N. Both boundaries include level N, so -mindepth 2 -maxdepth 2 selects exactly level 2. A minimum on its own sets no upper limit: -mindepth 1 skips the starting point but allows every deeper level.
Put these options after all starting paths and before tests such as -type f. They apply globally to the search; placing one later in the expression doesn't limit its effect to the tests that follow it. Both the GNU manual and the native macOS manual describe this behavior.
Build a tree with an empty branch and a hidden file
-maxdepth and -mindepth are extensions rather than POSIX requirements. They are available in GNU find and in the native macOS find used here; check your system's man find before running the examples.
Run setup where you can create a new find-depth-lab directory. It refuses an existing directory with that name, so it won't merge demonstration files into another tree.
python3 - <<'PY'
from pathlib import Path
root = Path("find-depth-lab")
root.mkdir()
project = root / "project"
project.mkdir()
files = [
"readme.txt",
".hidden.txt",
"build/output.txt",
"src/main.py",
"src/lib/helper.py",
"packages/api/config.json",
"packages/api/src/app.py",
]
for name in files:
path = project / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("demonstration only\n")
(project / "empty").mkdir()
PY
After setup succeeds:
cd find-depth-lab
The complete project tree contains seven regular files and eight directories, counting project itself. Trailing slashes mark directories:
project/
├── .hidden.txt
├── readme.txt
├── build/
│ └── output.txt
├── empty/
├── src/
│ ├── main.py
│ └── lib/
│ └── helper.py
└── packages/
└── api/
├── config.json
└── src/
└── app.py
Here is every entry grouped by depth when the starting point is ./project. Names in this table are relative to that starting point:
| Depth | Entries |
|---|---|
| 0 | ./project/ itself |
| 1 | .hidden.txt, readme.txt, build/, empty/, src/, packages/ |
| 2 | build/output.txt, src/main.py, src/lib/, packages/api/ |
| 3 | src/lib/helper.py, packages/api/config.json, packages/api/src/ |
| 4 | packages/api/src/app.py |
All fifteen find variants below were executed on this demonstration tree on November 2, 2026, using native macOS 26.6.1 arm64 /usr/bin/find. They were checked both through direct process arguments and through /bin/sh; setup used CPython 3.14.7. Every variant exited 0 with empty stderr. These are list-only commands and leave the tree unchanged.
Output blocks show complete result sets, sorted for comparison, except for the final example about traversal order. Your sibling entries may appear in a different order. The GNU behavior described here is documented in its manual; the recorded runs used native macOS find, not GNU find or GNU/Linux.
Compare the starting point, its children, and regular files
With a maximum of zero, only the starting point is eligible:
find ./project -maxdepth 0 -print
./project
Raise the maximum to one:
find ./project -maxdepth 1 -print
./project
./project/.hidden.txt
./project/build
./project/empty
./project/packages
./project/readme.txt
./project/src
The starting point still appears because depth 0 is within the maximum. empty qualifies even though it has no children. packages qualifies too, but its child api is at depth 2 and stays outside the search.
Now exclude depth 0 with the opening command:
find ./project -mindepth 1 -maxdepth 1 -print
./project/.hidden.txt
./project/build
./project/empty
./project/packages
./project/readme.txt
./project/src
To keep only immediate regular files, use the type test:
find ./project -maxdepth 1 -type f -print
./project/.hidden.txt
./project/readme.txt
This version doesn't need -mindepth 1 to exclude ./project: that starting point is a directory, so it fails -type f. Hidden filenames don't require an extra option here. find reaches .hidden.txt as an ordinary child of the supplied directory; there's no shell * in this command to leave it out.
Search exactly level two
A maximum of two includes regular files from both levels 1 and 2:
find ./project -maxdepth 2 -type f -print
./project/.hidden.txt
./project/build/output.txt
./project/readme.txt
./project/src/main.py
For an exact-depth search, set the minimum and maximum to the same number:
find ./project -mindepth 2 -maxdepth 2 -print
./project/build/output.txt
./project/packages/api
./project/src/lib
./project/src/main.py
The result contains two files and two directories. Depth limits don't select an entry type. Add -type f if you also want to exclude directories.
Remove the maximum and all deeper entries become eligible too:
find ./project -mindepth 2 -print
./project/build/output.txt
./project/packages/api
./project/packages/api/config.json
./project/packages/api/src
./project/packages/api/src/app.py
./project/src/lib
./project/src/lib/helper.py
./project/src/main.py
To reach helper.py, find passes through src at depth 1. -mindepth 2 suppresses tests and actions on src while allowing traversal through it. With a maximum of 2, traversal stops at src/lib: that directory is eligible, but its child helper.py is too deep.
Each starting point gets its own zero
Start directly at src and main.py moves from depth 2 to depth 1:
find ./project/src -maxdepth 1 -type f -print
./project/src/main.py
helper.py is now at depth 2 and remains outside this maximum. The printed path still contains project/src, but those components don't add to the depth. Start counting at the path supplied to find.
You can supply two starting points before the options:
find ./project/src ./project/packages/api -maxdepth 1 -type f -print
./project/packages/api/config.json
./project/src/main.py
Both starting directories are depth 0 for their own searches. config.json qualifies as an immediate child of api, although it was depth 3 when the starting point was ./project. api/src/app.py is two levels below api, so it doesn't qualify.
A starting point can also be a regular file:
find ./project/readme.txt -maxdepth 0 -type f -print
./project/readme.txt
The file is depth 0 and passes the type test. Give that same file a minimum of one:
find ./project/readme.txt -mindepth 1 -print
No output. Its only entry is below the minimum, and a regular file has no descendants to search. That empty result was a successful run, with status 0 and no error output.
A minimum depth can disable a shallow prune
Suppose you want regular files from depth 2 onward, while skipping build. This command looks plausible:
find ./project -mindepth 2 -path './project/build' -prune -o -type f -print
The expression before -o is the pruning branch: match the path, then prevent descent. The branch after -o prints regular files. Adjacent tests imply AND, so a failed path test skips -prune and lets the file branch run.
But the global minimum takes effect before either branch can run on a shallow entry. The checked result includes the build output:
./project/build/output.txt
./project/packages/api/config.json
./project/packages/api/src/app.py
./project/src/lib/helper.py
./project/src/main.py
./project/build is at depth 1. The minimum suppresses all tests and actions on that entry, including -path and -prune. Traversal continues into the directory. At depth 2, build/output.txt doesn't match the directory path, so evaluation reaches -type f -print and prints it.
Removing the minimum lets pruning run at the directory:
find ./project -path './project/build' -prune -o -type f -print
./project/.hidden.txt
./project/packages/api/config.json
./project/packages/api/src/app.py
./project/readme.txt
./project/src/lib/helper.py
./project/src/main.py
Now pruning runs on build and skips its contents. The depth-1 files return as well, so removing the minimum hasn't met the original request to select only deeper files. Moving -mindepth 2 to the other side of -o won't fix that: it is global wherever you place it.
If you only need to omit the build files from the result, keep the minimum and filter their paths:
find ./project -mindepth 2 -type f ! -path './project/build/*' -print
./project/packages/api/config.json
./project/packages/api/src/app.py
./project/src/lib/helper.py
./project/src/main.py
This checked command meets the output requirement: regular files at depth 2 or deeper, with build descendants excluded. It still traverses build; the negative path test filters results rather than pruning a directory. That distinction matters if the requirement is to avoid entering the directory at all.
When a shallow directory must be pruned, its pruning branch needs to remain eligible for evaluation. Work through the find -prune guide and expression precedence before combining further selection conditions.
-depth changes order, not the boundary
Despite its name, -depth doesn't mean “one level deep.” It processes a directory's contents before the directory itself, as described in the GNU find manual.
find ./project -maxdepth 1 -depth -print
Here is the recorded order, without sorting:
./project/empty
./project/packages
./project/readme.txt
./project/.hidden.txt
./project/build
./project/src
./project
The result still contains the starting point and its six immediate children. ./project comes after its children; grandchildren remain outside the maximum. Don't rely on the order among siblings when writing a script.
Seven predictions to make before running find
Use the unchanged tree, with ./project as the starting point unless a question specifies another. Write the paths or explanation before reading the answers.
- Does
-maxdepth 0 -printprint nothing, the starting point, or its immediate children? - Starting at
./project, which regular files match-maxdepth 1 -type f? Does the hidden file need a special flag? - Which four entries match
-mindepth 2 -maxdepth 2 -print, and why are two of them directories? - Does
-mindepth 2 -printincludesrc/lib/helper.py? Explain how it gets through the shallowersrcdirectory. - With starting points
./project/srcand./project/packages/api, which files match-maxdepth 1 -type f? Why doesconfig.jsonqualify now? - In the minimum-depth pruning command, why is
build/output.txtprinted even though-path './project/build' -pruneis present? - What changes when
-depthis added to the-maxdepth 1 -printcommand: selected paths, parent/child order, or both?
Answers
- The starting point:
./project. Zero is a valid level. Nothing below it is reached by this search. ./project/.hidden.txtand./project/readme.txt. Both are regular files at depth 1. This command traverses the supplied directory directly, so the hidden name needs no special flag../project/build/output.txt,./project/packages/api,./project/src/lib, and./project/src/main.py. Equal bounds choose exactly level 2. There is no type test to exclude the two directories,apiandlib.- Yes.
srcis traversed even though its tests and actions are suppressed at depth 1.src/libis at depth 2, andhelper.pyis at depth 3; both meet the minimum. No maximum stops descent. ./project/src/main.pyand./project/packages/api/config.json. Each is one level below its own starting point. The depth ofconfig.jsonrelative to the former starting point,./project, doesn't control this command.- The pruning action never runs on
build. That directory is at depth 1, below the minimum of 2. Traversal continues into it, and its regular file passes the file branch at depth 2. Status0doesn't establish that an intended exclusion worked. - Parent/child order changes; the selected set stays the same. The checked run still prints all seven eligible entries.
-depthputs the starting directory after its children;-maxdepth 1still keeps grandchildren outside the search.
Turn a missed decision into a recall prompt, such as “Where is zero when the starting point changes?” Come back to it later and answer before looking at the tree. Regular recall and spaced review help long-term retention: you practise reconstructing the rule after a gap instead of only recognizing a familiar command.
For broader practice, Nibomo's Linux Command Line Flashcards — 100 cards in English cover general terminal foundations, including quoted -name patterns and -type f. The deck isn't a dedicated depth-limit worksheet; all seven answers are above. If your next selection also depends on file age, the find -mtime guide handles that separate boundary.
Disclosure: Nibomo publishes this site and offers the linked deck in its own catalog. AI assisted substantially with research, drafting, editing, demonstration execution, and exercise construction; the illustration is AI-generated. Runtime evidence covers fifteen native macOS command variants on November 2, 2026. GNU documentation was consulted; no GNU/Linux runtime execution, human editorial review, speed benchmark, or learning-outcome study is claimed. The lab didn't test symlinks, permission failures, concurrent changes, or network filesystems.