pstree Command in Linux: Complete Guide to Process Tree Display and Parameters

pstree command in Linux and it perimeters

Flat process lists are useful, but sometimes what I actually need to see is relationships — which process spawned which, how deep a chain of forked workers goes, or whether a runaway script has left behind a tangle of orphaned children. That’s exactly what pstree is built for. It takes the same process information ps gives you and renders it as a visual, hierarchical tree instead of a flat table. Here’s my complete guide to using it well.

What is the pstree Command?

pstree displays currently running processes as a tree, rooted at init (PID 1) or, if given a PID or username, rooted at that specific process. Each child process is shown branching off from its parent, with identical or near-identical child processes automatically compressed into a single line with a multiplier — so instead of seeing 20 identical kworker lines, you see one line like kworker/u4:0 ×20 (20*[kworker/u4:0]).

It’s part of the psmisc package on most distributions, alongside tools like killall and fuser.

Basic Syntax

pstree [OPTIONS] [PID | USER]

If you give it a PID, it roots the tree at that process. If you give it a username, it shows only that user’s processes and their descendants.

A Typical Output

Here’s what a basic pstree output looks like (captured with ps -ef --forest, which produces conceptually the same hierarchy that pstree visualizes more compactly):

UID        PID  PPID  C STIME TTY          TIME CMD
root         2     0  0 01:36 ?        00:00:00 [kthreadd]
root         3     2  0 01:36 ?        00:00:00  \_ [pool_workqueue_release]
root         4     2  0 01:36 ?        00:00:00  \_ [kworker/R-rcu_gp]
root         5     2  0 01:36 ?        00:00:00  \_ [kworker/R-sync_wq]
root         9     2  0 01:36 ?        00:00:00  \_ [kworker/0:0-events_power_efficient]

With pstree proper, that same relationship is rendered more compactly, using ASCII/Unicode tree connectors, and it automatically groups identical siblings:

init-+-kthreadd-+-kworker/0:0
     |          |-kworker/0:1
     |          `-4*[kworker/R-...]
     |-sshd---sshd---bash---pstree
     `-systemd-journald

Each -+- marks a branching point, |- marks a sibling, and `- marks the last child in a group. The 4*[...] notation means four identical child processes were collapsed into a single entry — this compression is one of pstree‘s most useful features, since a busy system can easily have dozens of nearly identical worker or kernel threads that would otherwise clutter the screen.

Common Parameters

OptionDescription
-pShow PIDs alongside process names
-uShow the username that owns each process (only where it differs from the parent)
-aShow full command-line arguments for each process
-cDisable compression of identical subtrees — show every process individually
-hHighlight the ancestry path of the current process
-gShow process group IDs
-nSort output numerically by PID instead of alphabetically by name
-AUse ASCII characters for tree lines instead of Unicode line-drawing characters (better for older terminals or piping to files)
-GUse VT100 line-drawing characters
-lDon’t truncate long lines to fit the terminal width
-sShow parent processes of the specified process, going up the tree instead of down
-ZShow SELinux security context for each process

Practical Examples

Show the full tree with PIDs

pstree -p

This is what I use most often when I need to actually act on a process — kill it, renice it, attach strace to it — because I need the PID, not just the name.

Show a specific user’s processes only

pstree -p www-data

Extremely useful on a web server when I want to see exactly what Apache or Nginx worker processes and any spawned PHP-FPM or CGI children look like, without noise from every other user on the box.

Show full command lines

pstree -a

This reveals the actual arguments each process was launched with — critical when several processes share the same binary name but were started with different config files or flags.

Trace a process’s ancestry upward

pstree -s 4521

If I’ve found a suspicious or misbehaving process via top and want to know exactly what spawned it (a cron job? a login shell? a systemd service?), -s walks up the tree instead of down, showing me the full parent chain.

Highlight where the current shell sits in the tree

pstree -h

Handy for understanding exactly how deep your current SSH session sits within the process tree — useful when debugging shell nesting issues (e.g., screen inside tmux inside sudo -s).

ASCII-only output for logging or copy-pasting

pstree -A

I use this when piping pstree output into a text file or bug report, since Unicode tree-drawing characters can render as garbled boxes in some terminals, editors, or ticket systems.

How pstree Works Internally

pstree builds its tree by reading process information from /proc, the same virtual filesystem top and ps rely on. Specifically, it reads each process’s /proc/[pid]/stat file, which contains the process’s own PID and its parent’s PID (PPID). By walking every process and linking each one to its parent, pstree reconstructs the entire process hierarchy in memory before rendering it as ASCII/Unicode art.

The compression feature (collapsing identical siblings into N*[name]) works by comparing each new child’s command name and arguments against its siblings already added to the tree — if they match, pstree just increments a counter instead of adding a new line.

Real-World Use Cases

Diagnosing a runaway script that spawns many children: if a shell script or cron job goes wrong and starts forking uncontrolled child processes, pstree -p <pid-of-script> immediately shows me the blast radius — exactly which processes descend from the misbehaving parent, so I can kill the whole subtree cleanly.

Understanding service supervision: on a systemd-managed service, pstree -p $(systemctl show -p MainPID --value myservice) shows me every worker process spawned by that service, which is invaluable when a service is supposed to have a fixed number of workers but seems to be leaking processes.

Auditing a suspicious login session: during incident response, pstree -a -p <suspicious-pid> combined with -s to trace ancestry helps establish exactly how a process came to exist — whether it was launched from a legitimate cron job, an SSH session, or something injected another way.

Container debugging: inside a container’s PID namespace, pstree quickly reveals whether a supervisor process is correctly reaping zombie children, which is a very common source of container misbehavior.

Killing an Entire Process Subtree

A practical trick I use constantly — combine pstree -p with kill:

pstree -p 4521 | grep -oP '\(\K[0-9]+(?=\))'

This extracts every PID in the tree rooted at 4521, which I can then pipe into kill:

kill -TERM $(pstree -p 4521 | grep -oP '\(\K[0-9]+(?=\))')

Though for this specific job, kill -- -$(ps -o pgid= <pid> | tr -d ' ') (killing by process group) or pkill -P (killing by parent) are often cleaner, purpose-built alternatives.

Troubleshooting Common Issues

“pstree: command not found” — It’s not always installed by default, especially on minimal container images and stripped-down server installs. Install it via the psmisc package:

# Debian/Ubuntu
sudo apt-get install psmisc

# RHEL/CentOS/Fedora
sudo dnf install psmisc

# Arch
sudo pacman -S psmisc

Tree output looks garbled in a log file or ticket system — Unicode tree-drawing characters don’t always survive copy-paste or non-UTF-8 terminals. Use pstree -A for plain ASCII output instead.

Output is truncated on a wide process tree — By default, pstree truncates lines to fit your terminal width. Use -l to disable truncation, especially useful when redirecting output to a file.

Performance Considerations

pstree is lightweight — it’s a single read-and-render pass over /proc, not a continuously polling tool like top. On systems with an unusually large number of processes (tens of thousands, common on busy container hosts), building the full tree can take a noticeable moment, but this is still far cheaper than most alternatives.

Security Implications

Like ps and top, pstree respects standard Unix process visibility rules — an unprivileged user typically can’t see full command-line arguments (-a) for processes owned by other users if those processes were launched with argument-hiding measures, though process existence and ownership are generally visible to everyone. As with any process inspection tool, be cautious about who has access to a shell capable of running pstree -a, since command-line arguments frequently leak sensitive information like API keys or passwords passed insecurely on the command line.

pstree vs. Related Commands

CommandDifference
ps -ef --forestAchieves a similar tree-like view using a flat ps table with ASCII indentation, without the automatic sibling compression pstree offers
ps axjfAnother ps-based tree view, showing PGID/session info alongside the hierarchy
htopOffers an interactive, toggleable tree view (press t) alongside live resource stats — good for exploratory work, less scriptable than pstree
systemd-cglsShows the process tree organized by systemd’s control groups (cgroups) rather than strict parent-child PID relationships — extremely useful for seeing exactly which processes belong to which systemd service or container

Compatibility Across Distributions

pstree is part of the psmisc package and is available on essentially every major Linux distribution — Debian, Ubuntu, RHEL, CentOS, Fedora, openSUSE, Arch — though it is not always installed by default, particularly on minimal/server ISO images and container base images like alpine or slim Docker images. On Alpine-based systems, busybox provides a much simpler built-in alternative, but true pstree from psmisc needs to be added explicitly if you want the full feature set (compression, SELinux context display, etc.).

pstree Inside Containers and PID Namespaces

Containers use Linux’s PID namespace feature to give each container its own isolated view of the process tree — inside a container, the container’s main process appears as PID 1, even though on the host it has some much higher “real” PID. Running pstree inside a container only shows the processes visible within that container’s own PID namespace:

docker exec mycontainer pstree -p

This is genuinely useful for a very specific class of container bug: zombie process accumulation. Because a container’s PID 1 has the same reaping responsibilities a normal system’s init/systemd would have, containers that use a plain application binary as PID 1 (rather than a proper init system or a lightweight init wrapper like tini or dumb-init) can accumulate zombie child processes if that application doesn’t correctly call wait() on its children. pstree -p inside the container makes this immediately visible — a growing list of Z-state (zombie) entries hanging directly off PID 1 with no legitimate parent cleaning them up.

docker exec mycontainer ps aux | grep -c ' Z '

If this count keeps growing over time, it’s a strong signal the container needs a proper init process, which is exactly why docker run --init exists — it injects a minimal init (tini) as PID 1 specifically to handle zombie reaping and signal forwarding correctly on behalf of whatever your actual application process is.

Comparing pstree’s Compression Against ps –forest

One detail worth calling out explicitly: ps -ef --forest, which I mentioned as an alternative earlier, does not perform pstree‘s sibling-compression — every single process gets its own line, even if there are fifty identical kernel worker threads. On a busy system, this can mean the difference between a pstree view compact enough to read in a single terminal screen, versus a ps --forest view that scrolls for pages. This is the main practical reason I reach for pstree over ps --forest specifically when I need a quick, human-readable overview rather than complete raw data — and conversely, why I switch to ps --forest when I specifically need every individual process visible without any compression hiding detail I actually need.

# Compact, compressed overview
pstree

# Every single process shown individually, useful when compression would hide something important
ps -ef --forest

Combining pstree With Process Ancestry Investigation

During a security investigation or general “how did this process come to exist” debugging session, I often combine pstree -s (showing ancestors) with checking each ancestor’s start time and command line for a complete picture:

pstree -a -s -p 4521

This single command shows the entire ancestry chain of PID 4521, all the way up to init/systemd, along with full command-line arguments at every level. I’ve used exactly this combination to trace a suspicious process back through several layers — a cron job, which launched a wrapper script, which launched the actual suspicious binary — establishing the full provenance in one readable view rather than manually walking PPID values through repeated ps lookups.

Summary

pstree turns the flat, sometimes overwhelming process table into something I can actually read at a glance — which is invaluable when I’m trying to understand relationships between processes rather than just their individual resource usage. Between -p for actionable PIDs, -a for full command lines, and -s for tracing ancestry, it covers the vast majority of hierarchy-related troubleshooting I do on Linux servers.

References

  • Linux man-pages project, pstree(1): https://man7.org/linux/man-pages/man1/pstree.1.html
  • psmisc project homepage: https://gitlab.com/psmisc/psmisc
  • The Linux Kernel /proc filesystem documentation: https://www.kernel.org/doc/html/latest/filesystems/proc.html
  • Debian package documentation for psmisc: https://packages.debian.org/psmisc
Total
0
Shares

Leave a Reply

Previous Post
printenv command in Linux and it perimeters

printenv Command in Linux: Complete Guide to Environment Variables Display and Parameters

Next Post
reboot command in Linux and it perimeters

reboot Command in Linux: Complete Guide to System Restart, Options, and Parameters

Related Posts