Skip to main content

Linux command: tree

18 July 2026

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.

The right mental model: ls gives you one floor of a building at a time, while tree hands you the whole floor plan at once. You lose the fine detail that a long ls -l shows, but you gain the shape, and the shape is usually what you came for.

Back to top

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.

Back to top

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:

EraMilestone
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 top

4. 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 top

5. 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.

Back to top

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.

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.

Back to top

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:

NeedUseWhy
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.

Back to top

8. Best Practices

  • Start shallow with -L. On any unfamiliar directory, run tree -L 2 first 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 --gitignore in 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', not tree -P *.log, or the shell expands the glob before tree sees it.
  • Use --charset=ascii when you share. For an email, ticket, or README, plain ASCII lines survive any encoding; the fancy box characters may not.
  • Reach for -J for 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. tree is not part of coreutils, so a fresh server or container often needs apt install tree first.

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 top

9. Common Mistakes

9.1 Four Common Myths

MythReality
"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 / or tree in your home folder can print millions of lines. Always cap the depth with -L until you know the size.
  • Forgetting -a. Hidden files such as .env and .github are 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=ascii and the lines come back.
  • Unquoted glob patterns. An unquoted *.py is expanded by the shell first, so -P receives the wrong argument.
  • Confusing -s with directory totals. -s shows each file's own size; only --du adds up what a directory contains.
  • Expecting it to find or change files. tree only draws; use find, grep, or du when you need to search, read, or measure.
Back to top

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.

  • tree recurses 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, or pacman if 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 -h and --du for sizes, -p -u -g for permissions and owners, and -F for type marks.
  • For machines, use -J for JSON or -X for XML; for sharing, use --charset=ascii so 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.

Back to top
Linux command: tree
Peter Martin
Peter Martin
Joomla Specialist

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