There is a particular kind of pause that happens after cloning someone else’s repository. You know the answer is somewhere inside it, but the directory names do not yet tell a story. tree is useful in that moment. It does not search file contents or explain the architecture; it gives your eyes a map, and a good map makes the next question easier.
The examples below were executed with tree 2.1.1 on Ubuntu 24.04 against a disposable TypeScript-shaped fixture containing src, tests, docs, node_modules, hidden entries, and a symbolic link. Substitute your own project path when following along.
First, make sure this is the tree you think it is
command -v tree
tree --version/usr/bin/tree
tree v2.1.1Two lines, two different assurances
command -vreveals the executable selected by the current shell, which helps catch aliases, wrappers, or a missing command.tree --versionidentifies the feature set under test; options such as--gitignoreare not available in every older release.The locally tested Ubuntu package identifies itself as 2.1.1; your distribution may package another maintained version.
Install it only if the command is missing
sudo apt update
sudo apt install treeRisk level: caution. Review the command before running it.
What APT changes here
apt updaterefreshes repository metadata;apt installthen resolves and installs the package selected for your Ubuntu release.Review the source and transaction summary before confirming a package change.
On Fedora-family systems the package is also named
treeand is commonly installed with DNF; on Arch Linux it is available through pacman. Use the native package manager rather than an untrusted binary download.
The first map should be small enough to read
tree -L 2 lynxbee-tree-demolynxbee-tree-demo
├── README.md
├── docs
│ ├── archive
│ └── guide.md
├── node_modules
│ └── pkg
├── source-link -> src
├── src
│ ├── app.ts
│ └── lib
└── tests
└── app.test.ts
9 directories, 4 filesRead the shape before reading every name
-L 2limits descent to two levels below the named root; it does not mean “show two items.”Without a directory argument,
treebegins at the current working directory.The final report counts entries visited within the chosen view. Filters and depth limits therefore change the totals.
The arrow shows
source-linkis a symbolic link tosrc; default output names the target without descending through it.
Hidden files change the story—and the risk
tree -a -L 1 lynxbee-tree-demolynxbee-tree-demo
├── .env
├── .github
├── README.md
├── docs
├── node_modules
├── source-link -> src
├── src
└── tests
7 directories, 2 filesWhy -a deserves respect
-aincludes names beginning with a dot, though.and..themselves are never printed.Hidden names can reveal repository configuration, credentials files, private keys, editor state, and deployment details.
This example exposes the name
.env, not its contents. Even filenames can be sensitive in screenshots, tickets, or public documentation.Sanitize output before sharing it; never assume a directory listing is harmless because it contains no file contents.
Remove the branches that drown out the answer
tree -I 'node_modules|archive' lynxbee-tree-demolynxbee-tree-demo
├── README.md
├── docs
│ └── guide.md
├── source-link -> src
├── src
│ ├── app.ts
│ └── lib
│ └── config.ts
└── tests
└── app.test.ts
6 directories, 5 filesThe quotes are doing real work
-Iexcludes matching files or directories from the listing.Inside tree’s pattern language,
|separates alternatives, so this expression excludes eithernode_modulesorarchive.Single quotes prevent the shell from interpreting wildcard characters or the pipe before
treereceives them.Filtering output is not deletion. The excluded directories remain untouched on disk.
Ask for one kind of file and prune the empty scaffolding
tree -P '*.ts' --prune lynxbee-tree-demolynxbee-tree-demo
├── src
│ ├── app.ts
│ └── lib
│ └── config.ts
└── tests
└── app.test.ts
4 directories, 3 filesMatching is not searching file content
-Pincludes filenames matching tree’s wildcard pattern; it does not inspect the text inside those files.*.tsis quoted so the shell cannot expand it against only the current directory.--pruneremoves empty directories from the filtered presentation, which is whydocsandnode_modulesdisappear.Use
rgwhen the question is “which file contains this text?” andfindwhen you need richer predicates or actions. Usetreewhen relationships are the point.
Sometimes you want only the rooms, not what is inside them
tree -d -L 2 lynxbee-tree-demolynxbee-tree-demo
├── docs
│ └── archive
├── node_modules
│ └── pkg
├── source-link -> src
├── src
│ └── lib
└── tests
9 directoriesA directory-only view is an architecture sketch
-dsuppresses regular files, leaving a quick picture of module, package, documentation, and test boundaries.The symbolic link is counted as a directory entry in this view but is still shown as a link.
Combine
-dwith-L,-I, or--dirsfirstwhen preparing a concise project overview.
Add metadata when names alone cannot answer the question
tree -p -h -L 1 lynxbee-tree-demo[drwxr-xr-x 4.0K] lynxbee-tree-demo
├── [-rw-rw-r-- 0] README.md
├── [drwxrwxr-x 4.0K] docs
├── [drwxrwxr-x 4.0K] node_modules
├── [lrwxrwxrwx 3] source-link -> src
├── [drwxrwxr-x 4.0K] src
└── [drwxrwxr-x 4.0K] testsDo not mistake an entry size for directory usage
-pprints file type and permission bits in the familiarls -lstyle.-hmakes each entry’s own size readable; a directory’s entry size is not the total space consumed by everything beneath it.Use
--duwhen you need cumulative directory sizes, but expect tree to read the entire selected subtree before emitting output.For interactive disk-usage investigation, ncdu provides a purpose-built view.
Symbolic links are quiet until you ask tree to follow them
tree -l -L 2 lynxbee-tree-demo/source-linklynxbee-tree-demo/source-link
├── app.ts
└── lib
└── config.ts
2 directories, 2 filesFollowing links widens the walk
-lfollows symbolic links that point to directories; without it, tree prints the target path but does not descend.Tree detects recursive link loops, but following links can still traverse much more data than expected.
Add
-xwhen a diagnostic must remain on the current filesystem, especially near mounts or network filesystems.Check the root path before combining
-lwith an unrestricted depth or cumulative size calculation.
When another program needs the map
tree -J -L 1 lynxbee-tree-demo[
{"type":"directory","name":"lynxbee-tree-demo","contents":[
{"type":"file","name":"README.md"},
{"type":"directory","name":"docs"},
{"type":"directory","name":"node_modules"},
{"type":"link","name":"source-link","target":"src"},
{"type":"directory","name":"src"},
{"type":"directory","name":"tests"}
]},
{"type":"report","directories":6,"files":1}
]JSON changes the consumer, not the traversal
-Jemits a JSON array containing the tree plus a final report object.-L 1still controls traversal depth; output format does not make an unbounded walk cheap.Validate and parse the JSON instead of scraping Unicode branch characters in automation.
Treat filenames as untrusted data when generating HTML, dashboards, or shell commands from the output.
A few combinations worth remembering
tree --dirsfirst -L 2: place directories before files for an onboarding-friendly overview.tree --gitignore: honor supported.gitignorerules when the installed version provides this option.tree -Q: quote filenames, which helps expose spaces and unusual names unambiguously.tree -D --timefmt "%Y-%m-%d %H:%M": add formatted modification times.tree --filelimit 200: avoid descending into directories with more than 200 entries.tree -n: suppress color escape sequences when capturing output in logs or documentation.
Where tree stops being the right tool
Find files by size, type, owner, or age: use the Linux find command.
Measure disk consumption interactively: use ncdu.
List files installed by a package: query the package database with the installed-files workflow.
Search source text: use
rgor another content searcher; tree only knows directory metadata.Feed stable automation: prefer JSON or NUL-safe purpose-built tools over parsing decorative terminal output.
The small habit that makes tree useful
Do not begin by printing everything. Begin with the directory you care about, two levels deep, and one question in mind. Add hidden files, metadata, matching, or link traversal only when that question asks for them. The result stays readable—and an unfamiliar repository starts feeling less like someone else’s maze.
Validated reference
Option behavior and output were checked locally against tree 2.1.1 on Ubuntu 24.04 and against the installed tree(1) manual. Consult man tree on your machine for the authoritative behavior of its exact build.
Comments and corrections