opscape

Files and directories

tree

Draw a directory and everything below it as an indented tree.

tree [OPTION...] [DIRECTORY...]

11 of 11 examples run in Debian 13.7 on 2026-10-31 · manual: tree(1)

tree prints a directory hierarchy with line-drawing characters, followed by a count of directories and files. It is the fastest way to understand the layout of an unfamiliar project or to paste a structure into documentation.

It is not part of coreutils, so it may need installing (apt install tree, brew install tree). Like ls, it hides dotfiles unless you pass -a. The useful options limit depth (-L), filter by pattern (-P, -I), and add details (-h for sizes, -p for permissions).

For machine-readable output, tree -J prints JSON; for anything scripted, find is still the better tool.

Examples

Show a directory tree

tree src
explain
output
src
├── Makefile
├── app.py
├── lib
│   └── helpers.py
├── main.c
└── util.sh

2 directories, 5 files

Subdirectories are expanded in place. The summary counts the starting directory too, so src and lib make "2 directories".

Limit the depth

tree -L 1
explain
output
.
├── README.md
├── access.log
├── data
├── file with spaces.txt
├── link-to-notes -> notes.txt
├── logs
├── notes.txt
├── photos
├── scripts
├── src
└── todo.txt

6 directories, 6 files

-L 1 shows only the top level. Without it, tree . on a real project can scroll for thousands of lines. link-to-notes is drawn with its target and counted as a file.

Only directories

tree -d
explain
output
.
├── data
├── logs
├── photos
├── scripts
└── src
    └── lib

7 directories

-d drops files and shows the directory skeleton.

Include hidden files

tree -a -L 1 | head -5
explain
output
.
├── .env
├── README.md
├── access.log
├── data

-a shows names that start with a dot, such as .env, which plain tree hides. head keeps the first five lines.

Show sizes

tree -h photos
explain
output
[   5]  photos
├── [ 583]  IMG_2041.png
├── [1.5K]  beach.png
└── [2.3K]  forest.png

1 directory, 3 files

-h prints each size with a unit, like ls -lh; files under 1 KiB stay in bytes. The size shown for the directory itself is what the filesystem reports for it, not the total of its contents; --du would add that up.

Only files matching a pattern

tree -P '*.py' --prune
explain
output
.
└── src
    ├── app.py
    └── lib
        └── helpers.py

3 directories, 2 files

-P keeps files matching the wildcard (directories are always walked), and --prune removes directories left empty by the filter, so only the path to each Python file remains.

Hide files matching a pattern

tree -I 'photos|logs|data'
explain
output
.
├── README.md
├── access.log
├── file with spaces.txt
├── link-to-notes -> notes.txt
├── notes.txt
├── scripts
│   ├── backup.sh
│   └── greet.sh
├── src
│   ├── Makefile
│   ├── app.py
│   ├── lib
│   │   └── helpers.py
│   ├── main.c
│   └── util.sh
└── todo.txt

4 directories, 13 files

-I excludes names matching the pattern; | separates alternatives. On a real project, tree -I 'node_modules|.git' is the usual form.

Full paths, no indentation lines

tree -fi src
explain
output
src
src/Makefile
src/app.py
src/lib
src/lib/helpers.py
src/main.c
src/util.sh

2 directories, 5 files

-f prints each entry's path from the starting directory, and -i removes the drawing. The result is close to what find prints, in sorted order.

Permissions and owners

tree -pu scripts
explain
output
[drwxrwxr-x dev     ]  scripts
├── [-rwxrwxr-x dev     ]  backup.sh
└── [-rwxrwxr-x dev     ]  greet.sh

1 directory, 2 files

-p adds the permission string, -u the owner. Executable scripts show their x bits.

JSON output

tree -J src/lib
explain
output
[
  {"type":"directory","name":"src/lib","contents":[
    {"type":"file","name":"helpers.py"}
  ]}
,
  {"type":"report","directories":1,"files":1}
]

-J writes the tree as JSON, ready for jq. The last element is the summary report.

Plain ASCII drawing

tree --charset=ascii -L 1 logs
explain
output
logs
|-- app.log
|-- app.log.1
`-- error.log

1 directory, 3 files

--charset=ascii draws with |-- and `-- instead of box-drawing characters, safe for e-mails and old terminals.

Common pitfalls

  • tree without -L on a large directory (a home directory, node_modules) produces a flood of output; always start with -L 2.

  • -P patterns apply to files only, and the | alternation needs quoting so the shell does not treat it as a pipe.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • tree is a separate program on every platform, the same code on Linux and macOS; it is usually not installed by default.

Options worth knowing

A selection, described in our own words. For the full list, see tree(1) or tree --help.

OptionWhat it does
-aShow hidden files too.
-dList directories only.
-L LEVELDescend at most LEVEL directories deep.
-fPrint the full path prefix for each entry.
-iDo not print the indentation lines.
-P PATTERNList only files matching the wildcard PATTERN.
-I PATTERNDo not list files matching PATTERN.
--pruneRemove empty directories from the output.
-hPrint sizes in human-readable units.
-sPrint the size of each file in bytes.
--duReport each directory's total size.
-pPrint permissions for each entry.
-uPrint the owner of each entry.
-DPrint the modification date of each entry.
-tSort by modification time.
-FAppend / to directories, * to executables, like ls -F.
-JWrite the tree as JSON.
-XWrite the tree as XML.
-lFollow symlinks to directories.
-o FILEWrite the output to FILE.
--charset CHARSETUse CHARSET for the drawing characters, e.g. ascii.
--dirsfirstList directories before files.
--gitignoreSkip files that .gitignore files exclude.
--noreportOmit the file and directory count at the end.

Related