Linux command: tree
Run ls in a deep project and you get one flat layer at a time. You see the folders, then you step into one, then another, rebuilding the shape of the whole thing in your head. tree does that work for you. It walks the directory downward and draws the entire structure as a single indented diagram, with clean lines showing exactly what sits inside what. One command, and the layout of a whole project is on your screen.
1. The Basics
The job of tree is to show a directory and everything below it as a picture. You point it at a folder, and it prints a depth-indented listing with drawing lines that connect each item to its parent:
$ tree
.
└── project
├── docs
│ └── guide.md
├── README.md
├── src
│ ├── main.py
│ └── utils.py
└── tests
└── test_main.py
5 directories, 5 files
The single idea that makes tree different from ls is recursion into shape. Where ls shows one directory as a list, tree keeps descending into every subdirectory and expresses the nesting visually. The vertical bars and corners are not decoration; they are the grammar. A │ means "this branch continues below", a ├── means "here is an item and there are more siblings after it", and a └── means "here is the last item in this folder". Read those three marks and you can trace any file back up to the root.
The last line is the report: a running count of how many directories and files tree printed. It is a quick sanity check on the size of what you are looking at, and section 4 shows how to turn it off.
tree does not just list a folder; it draws it. The lines are the meaning, not the ornament.
Back to topThe right mental model:
lsgives you one floor of a building at a time, whiletreehands you the whole floor plan at once. You lose the fine detail that a longls -lshows, but you gain the shape, and the shape is usually what you came for.
2. Where the Name Comes From
The name is the picture. Draw a directory with its subdirectories branching out and its files hanging off the ends, and you have drawn a tree: a single root at the top, branches that split into more branches, and leaves at the tips. Computer scientists have called this exact structure a "tree" since the earliest days of the field, because it matches the botanical shape turned upside down.
A filesystem is a tree in this precise sense. There is one root, written /, every directory is a branch that can hold more branches, and every plain file is a leaf with nothing below it. The command does not invent the metaphor; it simply draws the tree that was always there. When you run tree, you are looking at the literal data structure your filesystem is built on.
So the name is as direct as a name can be. The tool shows you the tree, and it is called tree. There is no acronym and no hidden joke, just the plainest possible word for the thing on the screen.
3. A Short History
Unlike ls or cd, tree is not an original Unix command and it does not come from Bell Labs. It was written by Steve Baker and first released in the mid-1990s as a small, independent utility. The idea was modest: take the recursive listing that people were already building by hand with ls -R and pipes, and draw it properly with connecting lines so the structure could be read at a glance.
Because it was useful and tiny, it spread. Other contributors added pieces over the years: Francesc Rocher added HTML output, and Kyosuke Tokoro added support for different character sets and OS/2. That history is printed right in the version string:
| Era | Milestone |
|---|---|
| 1996 | Steve Baker releases the first tree: a recursive listing drawn with connecting lines |
| late 1990s | HTML output added, so a directory tree can be published as a clickable web page |
| 1990s-2000s | Character-set and OS/2 support added, so the drawing lines render correctly on many systems |
| 2020s | Version 2 arrives with JSON output, .gitignore filtering, and a new maintainer team |
| Today | tree ships in the repositories of every major distribution, though it is often not installed by default |
The version on this article's reference system is tree v2.1.1. The commands below are written for the 2.x series; a much older tree will lack the newer options such as -J and --gitignore, but every basic flag has behaved the same way for decades.
One practical consequence of this history matters more than any date: tree is not part of GNU coreutils. The everyday commands ls, cp, and rm are always present, but tree is a separate package that many minimal servers and containers do not include. If the command is missing, install it by name:
$ sudo apt install tree # Debian, Ubuntu
$ sudo dnf install tree # Fedora
$ sudo pacman -S tree # Arch Linux
Back to top4. Simple Use Cases
4.1 The Simplest Possible Use
Run tree with no arguments and it draws the current directory and everything below it:
$ tree
.
├── docs
│ └── guide.md
└── src
└── main.py
2 directories, 2 files
The single dot . at the top is the starting directory, the same . that means "here" everywhere else in the shell. To draw a different folder, name it as an argument:
$ tree /etc/nginx
/etc/nginx
├── nginx.conf
└── conf.d
└── default.conf
4.2 Limit How Deep It Goes
On a large project the full tree can scroll for pages. The -L flag (short for level) stops the descent after a set number of levels, which is the most useful flag tree has:
$ tree -L 1 # only the top level, like a quick ls
.
├── docs
├── README.md
├── src
└── tests
3 directories, 1 file
Reach for -L whenever you are exploring something unfamiliar. Start with -L 1 or -L 2 to see the overall shape, then descend into the one branch you care about.
4.3 Show Hidden Files
Like ls, tree hides files whose name begins with a dot by default. The -a flag (short for all) reveals them:
$ tree -a -L 1
.
├── .env
├── .gitignore
├── docs
├── README.md
└── src
This is how you see configuration files like .env and .gitignore, and also the .git folder, which is usually large and rarely something you want to expand. Section 5 shows how to hide it again while keeping the rest.
4.4 Directories Only
When you only care about the skeleton and not the individual files, the -d flag (short for directories) prints folders alone:
$ tree -d
.
├── docs
├── src
└── tests
3 directories
This gives you the map of a project without the clutter of every source file, which is exactly what you want when you are learning your way around a new codebase.
4.5 Turn Off the Summary Line
Every tree ends with the report line, such as 3 directories, 5 files. It is helpful on screen, but it gets in the way when you save a tree into a document or feed it to another tool. The --noreport flag removes it, leaving only the diagram:
$ tree -L 1 --noreport
.
├── docs
├── README.md
├── src
└── tests
Use it whenever the tree is going into a README, an email, or a script, where a trailing count line would only be noise.
Back to top5. Moderate Use Cases
5.1 Show Only Files That Match a Pattern
The -P flag (short for pattern) keeps only files whose name matches a shell glob. It is perfect for answering "where are all the Python files?":
$ tree -P '*.py'
.
├── docs
├── src
│ ├── main.py
│ └── utils.py
└── tests
└── test_main.py
Notice that empty folders like docs still appear, because -P only filters files, not directories. To hide the folders that end up with nothing in them, add --prune, which removes empty branches:
$ tree -P '*.py' --prune
.
├── src
│ ├── main.py
│ └── utils.py
└── tests
└── test_main.py
Always quote the pattern, as in '*.py', so the shell hands the glob to tree instead of expanding it against the current folder first.
5.2 Hide What You Do Not Want to See
The opposite of -P is -I (short for ignore), which drops every file or folder that matches the pattern. This is how you silence the noisy directories that dominate a real project:
$ tree -I 'node_modules|.git' -L 2
The | separates several patterns, so this one call hides both node_modules and .git. On a JavaScript project that single flag is the difference between a readable tree and ten thousand lines of dependencies.
5.3 Respect .gitignore Automatically
Version 2 added a smarter version of the same idea. The --gitignore flag reads the project's .gitignore files and hides exactly what Git already ignores, so your tree matches the files that actually belong to the project:
$ tree -a --gitignore
.
├── .gitignore
├── docs
├── README.md
└── src
├── main.py
└── utils.py
Note that --gitignore does not hide the .git folder itself; combine it with -I '.git' when you want a clean view of just the tracked layout.
5.4 Add Sizes and Human-Readable Units
The -s flag (short for size) prints each file's size in bytes, and -h (short for human-readable) rewrites those bytes as K, M, and G:
$ tree -h src
src
├── [ 5] main.py
└── [ 5] utils.py
The size in brackets is the size of each file alone. To see how much a whole directory takes up, including everything inside it, add --du (short for disk usage), which sums the contents up the tree just as the du command does:
$ tree -h --du
.
└── [ 16K] project
├── [4.0K] docs
└── [4.0K] src
20K used in 2 directories
5.5 Sort by Time Instead of Name
By default tree lists entries alphabetically. The -t flag (short for time) sorts by last modification instead, and -r (short for reverse) flips any sort, so -t -r puts the most recently changed files at the bottom where your eye lands:
$ tree -t -r
A related favourite is --dirsfirst, which groups all folders above all files at each level. Many people find that layout easier to scan than a strict alphabetical mix of files and folders.
6. Advanced Use Cases
6.1 Full Paths for Piping and Scripts
The drawing lines are lovely for human eyes but useless to another program. The -f flag (short for full path) prints the complete path of every entry, and -i (short for indentation off) removes the tree lines, leaving a plain list you can feed elsewhere:
$ tree -if
.
./docs
./docs/guide.md
./src
./src/main.py
./src/utils.py
Combined with -P this becomes a readable, structured alternative to a bare find for quick tasks. For real scripting, however, the structured outputs below are safer.
6.2 Machine-Readable Output: JSON and XML
When another program needs to consume the tree, ask for it in a real data format. The -J flag (short for JSON) prints the structure as JSON, and -X prints XML:
$ tree -J docs
[
{"type":"directory","name":"docs","contents":[
{"type":"file","name":"guide.md"}
]}
,
{"type":"report","directories":1,"files":1}
]
The JSON output nests contents arrays exactly like the visual tree nests its branches, so a script can walk the structure directly. Pipe it into a tool like jq to filter or reshape it. This is the correct way to let a program read a directory layout; parsing the drawn lines with the box characters is fragile and best avoided.
6.3 Publish a Tree as a Web Page
The -H flag (short for HTML) writes a complete HTML page, with each entry turned into a clickable link relative to the base address you give it:
$ tree -H '.' -o index.html # write a browsable listing to a file
The -o flag (short for output) sends the result to a file instead of the screen. Point a browser at that file and you get a navigable index of the directory, the same feature that once made tree a quick way to publish a download folder.
6.4 Extra Detail: Permissions, Owners, and Type Marks
tree can annotate each entry with the same metadata ls -l shows. The -p flag (short for permissions) prints the mode string, -u (short for user) prints the owner, -g (short for group) prints the group, and -F (short for file type, matching ls -F) appends a marker such as / for a directory or * for an executable:
$ tree -pF docs
docs
└── [-rw-rw-r--] guide.md
With these flags tree becomes a recursive ls -l that keeps the shape, which is handy when you are auditing permissions across a whole subtree at once.
6.5 When the Lines Look Like Garbage
The connecting lines are drawn with special box characters. If your terminal, font, or file encoding cannot show them, you get broken symbols like M-bM-^T instead of clean corners. The --charset option tells tree which character set to draw with, and plain ascii falls back to ordinary keyboard characters that render everywhere:
$ tree --charset=ascii docs
docs
`-- guide.md
Here the corners become `-- and the branches become |--. It is less pretty but perfectly portable, which is exactly what you want when you paste a tree into an email, a ticket, or a document that must survive any encoding.
6.6 Following Symbolic Links
By default tree shows a symbolic link but does not step through it. It prints the link and where it points, with an arrow, and stops there:
$ tree
.
├── link_to_real -> real
└── real
└── inside
└── file.txt
The -l flag (short for follow links) tells tree to treat a linked directory like a real one and descend into it, so its contents appear under the link as well:
$ tree -l
.
├── link_to_real -> real
│ └── inside
│ └── file.txt
└── real
└── inside
└── file.txt
Following links is powerful but risky, because a link can point back up to a folder that contains it, creating an endless loop. tree guards against this: it tracks each directory it has already visited and, when a link would send it somewhere it has been, it marks the entry and refuses to recurse rather than spinning forever:
$ tree -l
.
└── loopback -> .. [recursive, not followed]
The lesson is to use -l deliberately, only when you actually want linked directories expanded, and to trust the [recursive, not followed] note as a sign that the safety net did its job.
7. Something Most Users Do Not Know
7.1 The Lines Turn Off by Themselves
Like ls, tree colorizes its output using your LS_COLORS setting, but only when it is writing to a real terminal. The moment you pipe it into less or redirect it to a file, it quietly drops the color codes so they do not litter the text with escape sequences:
$ tree | less # colors off automatically, no garbage in the pager
This is the same well-mannered behavior ls has, and it explains a common confusion: color that appears on screen but vanishes in a saved file is not a bug. If you truly want the color codes preserved through a pipe, force them on with -C (short for color); to force them off even on a terminal, use -n (short for no color).
7.2 tree Can Read a Tree Back In
Almost nobody knows this: tree can build its diagram from a plain list of paths instead of a real directory, using --fromfile. Feed it the output of find or any list of paths, and it draws the tree those paths imply, even for files that are not on this machine at all:
$ find . -type f | tree --fromfile
.
├── docs
│ └── guide.md
└── src
└── main.py
This turns tree into a general-purpose way to visualize any hierarchy that can be written as a list of slash-separated paths, from a remote server's file list to the keys of a config file. The drawing engine does not care whether the paths are real.
7.3 Knowing Where tree Stops
Part of expertise is knowing which tool takes over when tree reaches its edge:
| Need | Use | Why |
|---|---|---|
| Find files by size, age, or owner and act on them | find |
tree displays a structure; it does not run tests or actions on matches |
| Search inside file contents | grep |
tree only knows names and metadata, never what a file contains |
| Measure and rank what uses the most disk | du |
tree --du shows sizes in place, but du is built for totals and sorting |
| A one-line listing with full detail | ls -l |
When you only need one directory, ls is faster to read and always installed |
A fast, .gitignore-aware search across a project |
fd |
A modern find replacement that is quicker and skips ignored files by default |
| A tree view with rich colors, icons, and Git status | eza --tree |
A modern ls replacement whose --tree mode adds detail tree does not show |
The healthy habit is to use tree to understand a layout and the other tools to act on it. Draw the shape with tree, then reach for find, grep, or du to do the real work. On systems where you can install them, fd and eza --tree are worth knowing as faster, friendlier cousins of the classic pair.
8. Best Practices
- Start shallow with
-L. On any unfamiliar directory, runtree -L 2first to see the shape, then descend into the one branch you care about. - Hide the noise. Add
-I 'node_modules|.git|__pycache__'so a real project's tree stays readable instead of drowning in dependencies. - Use
--gitignorein a repository. It shows the files that actually belong to the project without you having to list every junk folder by hand. - Quote your patterns. Write
tree -P '*.log', nottree -P *.log, or the shell expands the glob beforetreesees it. - Use
--charset=asciiwhen you share. For an email, ticket, or README, plain ASCII lines survive any encoding; the fancy box characters may not. - Reach for
-Jfor scripts. Never parse the drawn lines in a program; ask for JSON and let a proper parser read it. - Remember it may not be installed.
treeis not part of coreutils, so a fresh server or container often needsapt install treefirst.
When you need the full detail, the documentation is one command away:
$ man tree # the full manual with every option
$ tree --help # a quick summary of every flag
Back to top9. Common Mistakes
9.1 Four Common Myths
| Myth | Reality |
|---|---|
"tree is always available like ls." |
It is a separate package, not part of coreutils, and is often missing on minimal systems. |
"tree -P hides everything that does not match." |
It filters files only; empty directories still show unless you add --prune. |
| "The color just disappears when I save the output." | That is on purpose: tree drops color when writing to a pipe or file, like ls. |
| "I can parse the tree output in my script." | The drawn lines are fragile to parse; use -J for JSON or -X for XML instead. |
9.2 Other Traps to Avoid
- Running it at the wrong level.
tree /ortreein your home folder can print millions of lines. Always cap the depth with-Luntil you know the size. - Forgetting
-a. Hidden files such as.envand.githubare invisible by default, which can hide the very thing you are looking for. - Broken box characters. Garbled corners mean an encoding mismatch, not a broken file. Switch to
--charset=asciiand the lines come back. - Unquoted glob patterns. An unquoted
*.pyis expanded by the shell first, so-Preceives the wrong argument. - Confusing
-swith directory totals.-sshows each file's own size; only--duadds up what a directory contains. - Expecting it to find or change files.
treeonly draws; usefind,grep, orduwhen you need to search, read, or measure.
10. Summary
The tree command draws a directory and everything below it as a single indented diagram, turning the invisible shape of a project into a picture you can read at a glance.
treerecurses into every subdirectory and connects items with lines:│continues a branch,├──marks an item with more siblings, and└──marks the last one.- It is a separate package, not part of coreutils, so install it with
apt,dnf, orpacmanif it is missing. - Control the view with
-L(depth),-a(hidden files),-d(directories only), and--prune(drop empty branches). - Filter with
-P(keep matches),-I(hide matches), and--gitignore(respect the project's ignore rules). - Add detail with
-hand--dufor sizes,-p -u -gfor permissions and owners, and-Ffor type marks. - For machines, use
-Jfor JSON or-Xfor XML; for sharing, use--charset=asciiso the lines render anywhere.
This is the quick reference worth keeping:
tree draw the current directory and everything below
tree -L 2 stop after two levels deep
tree -a include hidden dotfiles
tree -d directories only, no files
tree -P '*.py' --prune keep only .py files, drop empty folders
tree -I 'node_modules|.git' hide noisy folders
tree --gitignore respect the project's .gitignore
tree -h --du human-readable sizes with directory totals
tree -pug show permissions, owner, and group
tree -J output as JSON for scripts
tree --charset=ascii portable lines for email and tickets
man tree the full manual
Once tree is in your toolkit, a strange directory stops being a maze you explore one cd at a time and becomes a map you can take in at a glance. If your servers have grown into a tangle that no one can quite picture anymore, drawing that out clearly, and keeping it tidy, is exactly the kind of work I help with.


Peter is a Joomla specialist and a Linux admin for fast, secure and scalable websites.


