On my OpenBSD system (also tested on Ubuntu):
$ ps -o pid=,ppid=,command= -ax | awk '{ parent[$1] = $2; cmd[$1] = $3 } function depth(p) { if (p == "" || p == 1) return 1; else return depth(parent[p]) + 1 } END { for (p in parent) print p, parent[p], depth(p), cmd[p] }' | mlr --n2p --barred label PID,PPID,Depth,Command then sort -n Depth,PID
+-------+-------+-------+---------------------------+ 01:51:28 [30/1957]
| PID | PPID | Depth | Command |
+-------+-------+-------+---------------------------+
| 1 | 0 | 1 | /sbin/init |
| 5555 | 1 | 2 | /usr/sbin/smtpd |
| 8624 | 1 | 2 | /usr/local/sbin/obsdfreqd |
| 11258 | 1 | 2 | ssh: |
...
| 25177 | 78190 | 4 | tmux: |
| 39399 | 42657 | 4 | zsh |
| 61862 | 42657 | 4 | awk |
| 90021 | 65371 | 4 | /usr/local/bin/syncthing |
+-------+-------+-------+---------------------------+
The command pipeline, pretty-printed:
ps -o pid=,ppid=,command= -ax |
awk '
{
parent[$1] = $2
cmd[$1] = $3
}
function depth(p) {
if (p == "" || p == 1)
return 1
else
return depth(parent[p]) + 1
}
END {
for (p in parent)
print p, parent[p], depth(p), cmd[p]
}' |
mlr --n2p --barred \
label PID,PPID,Depth,Command then \
sort -n Depth,PID
The ps
command provides the raw input data and does so by outputting the PID, PPID, and the command string of every process on the system.
This is read by awk
and we store the parent for each PID in the parent
array, and the first word of the command in the cmd
array. These arrays are keyed on the PID.
The awk
code then outputs the PID, PPID, depth, and the first word of the command string for each PID. The depth is calculated by the recursive function depth
, which traces the PID back to the process with PID 1 (or with no parent; this occurred on a system with lots of various containers running on it), and then propagates the accumulation of depth back to the caller.
The mlr
command at the end is purely for formatting and sorting the output. It uses Miller to read the space-delimited data, label it, sort it on the depth and PID fields, and then produce a pretty-printed tabular output. That mlr
command could probably be replaced with
sort -k 3,3n -k 1,1n | column -t
As for filtering on the depth, this would be trivial with Miller as you could just add another processing step with then
followed by e.g. filter '$Depth > 3'
.
You could also filter with awk
if you don't want to use Miller. Piping the output of column -t
through awk '$3 > 3'
would extract all lines with a depth greater than three. You could also make the print
statement of the larger awk
code conditional on the depth:
END {
for (p in parent)
if ((d = depth(p)) > 3)
print p, parent[p], d, cmd[p]
}