Linux tree Command Deep Dive: From Directory Traversal to Tree Visualization#

tree is one of my favorite Linux commands. While ls only shows one level, tree lets you see the entire directory structure at a glance—it’s incredibly useful for understanding project layouts.

The Core Value of tree#

When you inherit a complex project, ls only shows the top level:

ls my-project/
# src  public  package.json  node_modules

You can’t see how the project is organized. Switch to tree:

tree my-project/ -L 2 -I 'node_modules'
# my-project/
# ├── package.json
# ├── public/
# │   └── index.html
# └── src/
#     ├── components/
#     ├── hooks/
#     └── App.tsx

Crystal clear. That’s the core value of tree: display directory hierarchy as a tree structure.

Under the Hood: Recursive Directory Traversal#

The core algorithm of tree is depth-first search (DFS):

// Core logic of tree command (simplified)
void print_tree(const char *path, int level) {
    DIR *dir = opendir(path);
    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
        // Skip . and ..
        if (strcmp(entry->d_name, ".") == 0 || 
            strcmp(entry->d_name, "..") == 0) continue;
            
        // Print indentation and connectors
        for (int i = 0; i < level; i++) {
            printf("│   ");
        }
        printf("├── %s\n", entry->d_name);
        
        // If directory, recurse
        char fullpath[PATH_MAX];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
        
        if (entry->d_type == DT_DIR) {
            print_tree(fullpath, level + 1);
        }
    }
    closedir(dir);
}

Key points:

  1. readdir() system call reads directory contents
  2. d_type field determines file type without extra stat() calls
  3. Recursion depth determines indentation level
  4. Connectors use ├── for siblings and └── for last nodes

Common Options Explained#

-L Limit Depth#

When project directories are deep, limit the display depth:

tree -L 2 src/
# Only show 2 levels, avoid lengthy output

-I Exclude Patterns#

Exclude directories you don’t need to see:

tree -I 'node_modules|.git|dist' .
# Exclude node_modules, .git, dist directories

Supports glob patterns, use | to separate multiple patterns.

-d Directories Only#

Only care about directory structure, not files:

tree -d src/
# src/
# ├── components/
# ├── hooks/
# └── utils/

-a Show Hidden Files#

By default, files starting with . are hidden:

tree -a .
# Will show .env, .gitignore and other hidden files

-p Show Permissions#

See permissions for each file at a glance:

tree -p src/
# src/
# ├── [drwxr-xr-x]  components/
# └── [-rw-r--r--]  App.tsx

-s Show File Size#

tree -s -h src/
# -h makes sizes readable (KB/MB)
# src/
# ├── [   4.0K]  components/
# └── [   2.3K]  App.tsx

Practical Use Cases#

1. Quickly Understand Project Structure#

tree -L 2 -I 'node_modules|.next|dist' .

When starting with a new project, this command helps you understand the organization in 30 seconds.

2. Generate README Directory Structure#

When writing technical docs, you need to show directory structure:

tree -I 'node_modules' --noreport --charset ascii

--noreport skips the final statistics, --charset ascii ensures compatibility.

3. Find Specific File Types#

tree -P '*.tsx' src/
# Only show .tsx files

-P is the include pattern, -I is the exclude pattern.

4. Count Files in Directory#

tree --dirsfirst -F | grep -c /

Combined with grep to count directories.

Performance Considerations#

Large Directory Performance#

tree recursively traverses all subdirectories. If deeply nested or with many files, it can be slow:

# Never run tree on root directory
tree /  # Will traverse entire filesystem

Recommendations:

  1. Always use -L to limit depth
  2. Use -I to exclude large directories
  3. Only run in project root

Directories may have circular symbolic links. tree follows them by default:

tree -l .  # -l follows symlinks, may get stuck in loops

Use -P or avoid -l to prevent issues.

Web Implementation Approach#

To implement similar functionality in browser, use File System Access API:

async function buildTree(dirHandle: FileSystemDirectoryHandle, depth = 0): Promise<TreeNode[]> {
  if (depth > 10) return [] // Limit depth to prevent stack overflow
  
  const nodes: TreeNode[] = []
  
  for await (const entry of dirHandle.values()) {
    const node: TreeNode = {
      name: entry.name,
      type: entry.kind,
      children: []
    }
    
    if (entry.kind === 'directory') {
      node.children = await buildTree(entry, depth + 1)
    }
    
    nodes.push(node)
  }
  
  return nodes.sort((a, b) => {
    // Directories first
    if (a.type !== b.type) return a.type === 'directory' ? -1 : 1
    return a.name.localeCompare(b.name)
  })
}

Frontend rendering with recursive component:

function TreeNode({ node, level }: { node: TreeNode; level: number }) {
  const [expanded, setExpanded] = useState(level < 2)
  const isDir = node.type === 'directory'
  
  return (
    <div style={{ paddingLeft: level * 16 }}>
      <div onClick={() => isDir && setExpanded(!expanded)}>
        {isDir ? (expanded ? '▼' : '▶') : '•'} {node.name}
      </div>
      {expanded && node.children?.map((child, i) => (
        <TreeNode key={i} node={child} level={level + 1} />
      ))}
    </div>
  )
}

tree vs find vs ls#

Command Use Case Output Format
ls View single level List/Grid
tree View multi-level structure Tree diagram
find Search files Path list

They complement each other:

  • ls for quick look
  • tree for understanding structure
  • find for precise search

Pro Tips: Beautifying tree Output#

Add Colors#

tree -C .  # Colorized output

Different file types show different colors—blue for directories, green for executables, etc.

Output to File#

tree > structure.txt  # Export directory structure to file

Great for generating project documentation.

Show Recently Modified Files#

tree -D src/  # Show modification time
tree -c -D src/ | head -20  # Sort by time, get recent 20

Summary#

The tree command solves the “understand directory structure” problem through tree visualization. Its core value isn’t technical complexity, but making file system hierarchy intuitive for humans.

Next time you pick up a new project, run tree -L 2 -I 'node_modules' . first—you’ll grasp the project skeleton in 30 seconds.


Related Tools: