opscape

Processes and jobs

ps

Print a snapshot of running processes, with the columns, filters and sort order you choose.

ps [OPTION...]

12 of 12 examples run in Debian 13.7 on 2026-10-31 · procps 2:4.0.4-9 · manual: ps(1)

ps takes one snapshot of the process table and prints it. With no options it shows only the processes attached to your current terminal, which is rarely what you want. The two everyday forms are ps -ef (every process, System V style) and ps aux (every process, BSD style, with CPU and memory columns). After that, -o is the option worth learning: it picks exactly the columns you want, and --sort orders them, which turns ps into a precise query tool for scripts.

The procps ps on Linux accepts three option dialects at once. Options with one dash (-e, -f, -o) are the Unix/POSIX style; options with no dash (aux, axjf) are the old BSD style; options with two dashes (--sort, --forest) are GNU long options. They are not interchangeable: ps -aux and ps aux mean different things, and ps a and ps -a select different processes. In BSD style, a means "all users' processes that have a terminal", x adds the ones without a terminal (daemons, cron jobs, anything started from a script) and u switches to the user-oriented column set (USER, %CPU, %MEM, VSZ, RSS, STAT, START). So aux reads as "all processes, terminal or not, in user format". Likewise ps axjf is a+x, j for job-control columns (PPID, PGID, SID) and f for an ASCII-art forest.

The examples run inside a container, so the process list is short: the sandbox's runner (PID 1), sshd, and the bash -c shell that runs each example, which is why the example's own command line shows up in the listings. For a live, refreshing view use top; to find PIDs by name use pgrep; for the parent/child tree, pstree draws it more compactly.

Examples

Show the processes started from this shell

sleep 300 &
ps
kill %1
output
    PID TTY          TIME CMD
   1886 ?        00:00:00 bash
   1887 ?        00:00:00 sleep
   1888 ?        00:00:00 ps

Plain ps lists processes with your user ID on the same terminal as ps. The examples run without a terminal (TTY is ?), so it shows the shell, the background sleep and ps itself. TIME is CPU time used, not how long the process has existed. The last line cleans up the background job.

List every process on the system

ps -ef
explain
output
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0 21 20:19 ?        00:00:15 python3 /opt/runner/run.py
root           8       1  0 20:19 ?        00:00:00 sshd: /usr/sbin/sshd [listen
dev         1889       1  0 20:20 ?        00:00:00 ps -ef

-e selects every process, -f adds the full-format columns: owner, PPID (parent PID), C (CPU use as a lifetime percentage), start time (STIME) and the full command line. In this container that is the root-owned runner, sshd and ps itself. A real server shows hundreds of lines, including kernel threads in square brackets.

List every process with CPU and memory use (BSD style)

ps aux
explain
output
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1 21.8  0.0 252788 18160 ?        Rsl  20:19   0:15 python3 /opt/
root           8  0.0  0.0  11768  3356 ?        Ss   20:19   0:00 sshd: /usr/sb
dev         1890  0.0  0.0   6788  3068 ?        R    20:20   0:00 ps aux

No dash: these are BSD options. %CPU and %MEM are percentages, VSZ the virtual size and RSS the resident memory, both in KiB. STAT codes: S sleeping, R running, s session leader, + foreground on a terminal. The command column is cut at the terminal width (80 here); add ww (ps auxww) for full command lines.

Choose exactly which columns to print

sleep 300 &
ps -o pid,ppid,user,stat,etime,args
kill $!
output
    PID    PPID USER     STAT     ELAPSED COMMAND
   1891       1 dev      S          00:00 /bin/bash -c sleep 300 & ps -o pid,ppi
   1892    1891 dev      S          00:00 sleep 300
   1893    1891 dev      R          00:00 ps -o pid,ppid,user,stat,etime,args

-o takes a comma-separated list of column names. etime is elapsed time since the process started ([[dd-]hh:]mm:ss) and args the full command line; comm would give just the program name. Without a selection option, -o still applies to the current terminal's processes.

Show processes by command name

sleep 300 & sleep 600 &
ps -C sleep -o pid,etimes,args
output
    PID ELAPSED COMMAND
   1895       0 sleep 300
   1896       0 sleep 600

-C NAME selects processes whose command name is exactly NAME, with no pattern matching and no risk of matching the grep in ps | grep. etimes is elapsed time in plain seconds, easier to compare in a script than etime.

Get a bare value for a script

sleep 300 &
ps -o pid= -o etimes= -p $!
echo "state: $(ps -o stat= -p $!)"
output
   1898       0
state: S

A column name followed by = with nothing after it prints no header, so the output is just the value. -p PID selects one process. The spaces before the numbers are column padding; trim them or use $(( )) if you need a number.

Find the processes using the most memory

ps -eo pid,user,rss,comm --sort=-rss | head -5
explain
output
    PID USER       RSS COMMAND
      1 root     18164 python3
      8 root      3356 sshd
   1902 dev       3004 ps
   1901 dev       2412 bash

--sort=-rss sorts by resident memory, largest first (a leading - reverses the order; + or nothing is ascending). head -5 keeps the header and the top four. On a real server this is the quickest answer to "what is eating the RAM".

Find the processes using the most CPU (BSD style)

ps aux --sort=-%cpu | head -4
explain
output
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1 21.9  0.0 252788 18164 ?        Ssl  20:19   0:15 python3 /opt/
root           8  0.0  0.0  11768  3356 ?        Ss   20:19   0:00 sshd: /usr/sb
dev         1904  0.0  0.0   4488  2400 ?        S    20:20   0:00 /bin/bash -c 

BSD selection with a GNU long option mixed in, which procps allows. %CPU is CPU time divided by the process's lifetime, an average since it started, not the current load; for "right now" use top.

Show parent and child processes as a tree

(sleep 300 & sleep 400 & wait) &
sleep 0.2
ps -u dev --forest -o pid,ppid,args
kill %1
output
    PID    PPID COMMAND
   1907       1 /bin/bash -c (sleep 300 & sleep 400 & wait) & sleep 0.2 ps -u de
   1908    1907  \_ /bin/bash -c (sleep 300 & sleep 400 & wait) & sleep 0.2 ps -
   1910    1908  |   \_ sleep 300
   1911    1908  |   \_ sleep 400
   1912    1907  \_ ps -u dev --forest -o pid,ppid,args

--forest indents children under their parent with \_. The subshell in parentheses is a forked copy of the shell, so it shows the same command line; it is the parent of the two sleep processes, and its wait keeps it alive. -u dev selects by effective user.

See when a process started

sleep 300 &
ps -o pid,lstart,etime,args -p $!
output
    PID                  STARTED     ELAPSED COMMAND
   1914 Sat Oct 31 20:20:33 2026       00:00 sleep 300

lstart is the full start timestamp, etime the elapsed time since. Both are computed from the kernel's boot-relative start time, so they are correct even if the clock was changed after the process started.

Show the threads of a process

python3 -c 'import threading, time
for _ in range(3): threading.Thread(target=time.sleep, args=(30,), daemon=True).start()
time.sleep(30)' &
sleep 0.5
ps -L -o pid,lwp,nlwp,comm -p $!
output
    PID     LWP NLWP COMMAND
   1916    1916    4 python3
   1916    1918    4 python3
   1916    1919    4 python3
   1916    1920    4 python3

-L prints one line per thread. LWP is the thread ID and NLWP the thread count: the main Python thread plus the three it started share one PID.

Check whether a PID is still running

ps -p 99999 -o pid,args; echo "exit status: $?"
explain
output
    PID COMMAND
exit status: 1

When nothing matches, ps prints only the header and exits with status 1, which makes ps -p "$pid" >/dev/null a usable liveness test. kill -0 "$pid" does the same job without starting another program.

Common pitfalls

  • ps aux | grep nginx also matches the grep process itself. Use pgrep -a nginx or ps -C nginx, or the old trick grep '[n]ginx', whose own command line does not match its pattern.

  • ps -aux is not ps aux. With a dash it means -a plus -u x, processes of a user named "x". procps guesses you meant aux when no such user exists, but other systems (and a machine with a user x) will not. Type ps aux or ps -ef.

  • The command column is cut at the terminal width (or at COLUMNS, which is 80 in these examples), so long Java or Python command lines lose the part you wanted. Add ww (ps auxww, ps -eo pid,args ww) for unlimited width.

  • %CPU is a lifetime average. A process that just started spinning shows a low value; use top -b -n 1 or pidstat for current usage.

  • Parsing the default output with awk '{print $2}' breaks when a column is empty or the layout differs between systems. Ask for exactly what you need: ps -o pid= -C name.

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

  • macOS and BSD ps support -ef, aux, -o, -p and -u, but not the GNU long options: no --sort, --forest or --no-headers. On macOS sort with -m (memory) or -r (CPU), or pipe to sort.

  • -C NAME (select by command name) is procps-only; on macOS, -C means something unrelated. Use pgrep there.

  • -L on macOS lists the column keywords -o accepts instead of threads; ps -M shows threads there. For portable scripts stick to POSIX column names: ps -o pid,ppid,user,etime,args works on both.

Options worth knowing

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

OptionWhat it does
-eSelect every process (same as -A).
-ASelect every process (same as -e).
-fFull format: UID, PPID, start time and full command line.
-FExtra full format: adds memory size, RSS and the CPU it last ran on.
-lLong format: adds state, priority, nice value and wait channel.
-o, --format COLUMNSPrint the comma-separated list of columns given; name= hides a header.
-p, --pid PIDLISTSelect processes by PID.
--ppid PIDLISTSelect processes whose parent is in PIDLIST.
-C CMDLISTSelect processes by exact command name.
-u, --user USERLISTSelect processes by effective user name or ID.
-U, --User USERLISTSelect processes by real user name or ID.
-g, --group GRPLISTSelect by session or effective group name.
-t, --tty TTYLISTSelect processes attached to the given terminals.
-s, --sid SESSLISTSelect processes in the given sessions.
-aSelect all processes with a terminal except session leaders.
-dSelect all processes except session leaders.
-N, --deselectInvert the selection.
-HIndent children under their parents (process hierarchy).
--forestDraw the process tree with ASCII art.
--sort SPECSort by columns, e.g. -rss or +pid,-etime.
-LShow one line per thread, with LWP and NLWP columns.
-TShow threads with an SPID column.
-jJobs format: PGID and SID columns.
-yWith -l, show RSS instead of flags and ADDR.
-wWide output; give it twice for unlimited width.
--no-headersDo not print the header line.
--headersRepeat the header line once per page of output.
-q, --quick-pid PIDLISTSelect by PID in quick mode, skipping some slow lookups.
-V, --versionPrint the procps version.

Related