ps

Show a snapshot of running processes

Updated 2026-08-24

ps prints the process table as it stood at the moment the command ran. Nothing about it updates, and a process that started and exited a second earlier leaves no trace in it. Processes and signals explains what those entries are, including the letters in the STAT column.

Three option syntaxes

Most of the confusion around ps comes from it accepting three separate option styles. BSD options take no dash (ps aux), UNIX options take one (ps -ef), and GNU long options take two (ps --sort=-%mem).

The same letter can mean different things across them. u in BSD syntax asks for the user-oriented column set, while -u in UNIX syntax takes a username and selects that person's processes. ps aux and ps -u alice are both correct and have almost nothing to do with each other.

ps aux and ps -ef are the two listings people tend to use most often, and both show every process. They differ in the columns they output: aux prints %CPU, %MEM and the start time, -ef the parent PID.

What bare ps selects

ps on its own shows processes that both belong to you and are attached to your terminal, which at a prompt means your shell and ps itself. -e, or its synonym -A, selects every process on the system.

Choosing the columns

-o replaces the default column set entirely: ps -eo pid,user,comm. A trailing = suppresses a column's header, so -o comm= prints one bare name per line with nothing to strip, which is handy for feeding ps output to xargs.

Two columns are easy to confuse. comm is the executable's name, truncated to 15 characters. args is the full command line it was invoked with. A shell script reports its own name under comm and its interpreter plus path under args, so a search that works against one may find nothing against the other.

What %CPU measures

The %CPU column is the process's accumulated CPU time divided by how long it has been alive. That makes it an average across the whole lifetime rather than a reading of what the process is doing now: something that saturated a core this morning still reports a high figure tonight.

Finding a process by name

ps aux | grep sshd also matches the grep, because grep sshd has sshd in its own command line. pgrep sshd answers the question directly, never matches itself, and prints PIDs ready for killing whatever holds a port. pgrep and its signal-sending twin are covered on kill, which is where the selection options they share are set out.

Sample files used on this page

Every example below was run against these files. Recreate them to follow along.

three processes owned by user the page needs something to select that is not part of the boot, so the setup script starts a small tree: tips-supervisor is a shell script that spawns two copies of tips-backup and waits. The examples run as root, which is why -u user matches these three and nothing else.

user     tips-supervisor /bin/bash /usr/local/bin/tips-supervisor
user     tips-backup     /usr/local/bin/tips-backup 3600
user     tips-backup     /usr/local/bin/tips-backup 3600
53 outputs, collapsed by default

The two standard listings

aux is BSD syntax and -ef is UNIX syntax. Both select every process; they differ in which columns they print. PIDs and times below are from one run and will differ on your machine.

List every process, BSD style

ps aux | head -4

The listing people reach for most. a drops the same-user restriction, u asks for the user-oriented columns, and x includes processes with no controlling terminal, which is most daemons.

Show output

Your output will differ: the PIDs, start times and CPU and memory figures are specific to your machine, as is which process follows cron

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1 21.1  0.3  23028 12988 ?        Ss   11:43   0:00 /lib/systemd/systemd
root          35 18.5  0.3  34788 12200 ?        Ss   11:43   0:00 /usr/lib/systemd/systemd-journald
root          87  0.0  0.0   4400  2484 ?        Ss   11:44   0:00 /usr/sbin/cron -f

List every process, UNIX style

ps -ef | head -4

The same set of processes with a different column choice. -e selects everything and -f asks for the full-format listing, whose distinguishing column is PPID, the parent process id.

Show output

Your output will differ: the PIDs and start times come from one run, and your machine will list different processes after cron

UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0 20 11:43 ?        00:00:00 /lib/systemd/systemd
root          35       1 18 11:43 ?        00:00:00 /usr/lib/systemd/systemd-journald
root          87       1  0 11:44 ?        00:00:00 /usr/sbin/cron -f

List everything the init system started

ps --ppid 1 -o comm= | sort -u

--ppid 1 selects the children of PID 1, which is close to a list of the machine's daemons. sort -u does more on a machine with virtual consoles than it does here: each console runs its own agetty, so the raw listing repeats that one name five or six times before anything else appears.

Show output
cron
sshd
systemd-journal
tips-supervisor

Count how many processes are running

ps -e --no-headers | wc -l

--no-headers removes the column header so every line is a process. The count includes ps itself and the shell that ran it.

Show output

Your output will differ: the total depends on what your machine is running

16

Look at a single process by PID

ps -o pid,comm -p 1

-p selects by process id. PID 1 is the init system, which on Debian is systemd.

Show output
    PID COMMAND
      1 systemd

Show a process with its full command line

ps -q 1 -o comm=,args=

-q is -p without the reordering, and args is the command line as invoked. Here it shows that the systemd shown by comm was started as /lib/systemd/systemd.

Show output
systemd         /lib/systemd/systemd

Check whether a named service is running

ps -o comm= -C cron

-C selects by command name. Nothing is printed and the exit status is non-zero when no process matches, which makes it usable in a test. For a service under systemd, systemctl is-active answers a more useful question.

Show output
cron

Columns and fields

-o replaces the default column set with the one you name. A trailing = on a field drops its header, and a field with nothing after the = produces output with nothing to strip before using it.

Show who owns a process

ps -o user=,group=,comm= -C tips-supervisor

user and group are the effective ids, resolved to names. Add ruser and rgroup for the real ids, which differ for anything running setuid.

Show output
user     user     tips-supervisor

Tell the executable apart from the command line

ps -o comm=,args= -C tips-supervisor

comm is the executable's name and args is how it was invoked. A shell script reports its own name under comm and its interpreter under args, so a search matching one can miss the other.

Show output
tips-supervisor /bin/bash /usr/local/bin/tips-supervisor

Widen a column that is being truncated

ps -o "pid,comm:20,nice" -C tips-backup --no-headers

comm:20 sets the field width. comm itself is capped at 15 characters by the kernel, so a longer name needs args rather than a wider column.

Show output

Your output will differ: the PIDs are specific to your machine

    751 tips-backup            0
    752 tips-backup            0

Show the process state

ps -o stat=,comm= -C tips-backup

S is interruptible sleep, which is what a process waiting on something looks like. R is running, Z is a zombie, and a trailing s means session leader.

Show output
S    tips-backup
S    tips-backup

Show a process's scheduling class and niceness

ps -o cls=,ni=,comm= -C tips-backup

TS is the ordinary time-sharing class and ni is the nice value, where 0 is the default and higher means lower priority.

Show output
 TS   0 tips-backup
 TS   0 tips-backup

Count a process's threads

ps -o thcount=,comm= -C tips-supervisor

thcount (also spelled nlwp) is the number of threads. A single-threaded process reports 1.

Show output
    1 tips-supervisor

Show resident memory for one process

ps -o comm=,rss= -C tips-supervisor

rss is resident set size in kilobytes, meaning the memory actually in RAM. It double-counts pages shared between processes, so summing it across a system overstates the total.

Show output

Your output will differ: the memory figure depends on your machine

tips-supervisor  2968

Show CPU and memory percentages together

ps -o comm=,pcpu=,pmem= -C tips-supervisor

pcpu is the same figure %CPU shows in ps aux, averaged over the process's whole life rather than sampled now.

Show output

Your output will differ: the percentages depend on your machine

tips-supervisor  0.0  0.0

Show how long a process has been running

ps -o etimes=,comm= -C tips-supervisor

etimes is elapsed seconds, which sorts and compares numerically. etime gives the same span formatted as [[dd-]hh:]mm:ss, which reads better but does not sort.

Show output

Your output will differ: the elapsed time depends on when the process started

    556 tips-supervisor

Show the arguments a process was given

ps -o args= -C tips-backup | sort -u

The copies were started the same way, so sort -u collapses them to one line. The arguments are read from the process's own memory, so a process that rewrites its argument vector can hide them.

Show output
/usr/local/bin/tips-backup 3600

Selecting which processes

Selection options combine as a union rather than an intersection: naming both a user and a command name gives you everything matching either, not the overlap.

Select every process owned by a user

ps -u user -o user,comm --no-headers

-u takes the effective user, which is the one that matters for permissions. -U selects by real user instead.

Show output
user     tips-supervisor
user     tips-backup
user     tips-backup

Select by command name

ps -C tips-backup -o pid,comm --no-headers

-C matches the executable name exactly, so it neither needs nor accepts a pattern. It is the option that makes ps | grep unnecessary in most cases.

Show output

Your output will differ: the PIDs are specific to your machine

    751 tips-backup
    752 tips-backup

Select several command names at once

ps -o comm= -C tips-backup,tips-supervisor

-C takes a comma-separated list. The rows arrive in PID order rather than in the order you named them, so the supervisor leads here because it started first.

Show output
tips-supervisor
tips-backup
tips-backup

Select several PIDs at once

ps -o comm= -p 1,$(pgrep -x tips-supervisor)

-p also takes a list. Command substitution supplies a PID that is not known in advance, which is the shape most scripts need.

Show output
systemd
tips-supervisor

List the children of a process

ps --ppid "$(pgrep -x tips-supervisor)" -o comm=

--ppid selects by parent, which answers what a supervisor has actually started rather than what it claims to manage.

Show output
tips-backup
tips-backup

Count processes per user

ps -eo user= | sort | uniq -c

A quick way to see who is using a shared machine. The counts include the pipeline's own processes, which belong to whoever ran it.

Show output

Your output will differ: the counts depend on what your machine is running

     14 root
      3 user

Count only the processes matching a name

ps -e -o comm= | grep -c tips

Counting with grep -c rather than listing. This one is safe from the usual ps | grep trap because grep's own name does not contain the pattern.

Show output
3

Check whether a process has a controlling terminal

ps -o tty=,comm= -C tips-backup,tips-supervisor | sort -u

? in the TTY column means no controlling terminal. Every daemon shows ?, while anything started at a prompt shows the terminal it was started from.

Show output
?        tips-backup
?        tips-supervisor

Sorting

--sort takes a field name, and a leading - reverses it. It is a GNU long option, so it works alongside either of the other two syntaxes.

Find the processes using the most memory

ps -eo comm=,rss= --sort=-rss | head -3

Sorting by resident set size descending. This is the question ps answers better than anything else, because the sort happens before the truncation.

Show output

Your output will differ: which processes use most memory, and how much, depends on your machine

systemd         12996
systemd-journal 12260
sshd             7704

Find the processes using the most memory, as a percentage

ps -eo comm,%mem --sort=-%mem --no-headers | head -3

%mem is rss as a share of physical memory. A field name containing % works unquoted here, but quote it if your shell is configured to expand it.

Show output

Your output will differ: the processes and percentages depend on your machine

systemd          0.3
systemd-journal  0.3
sshd             0.1

Find the longest-running processes

ps -eo comm,etime --sort=-etime --no-headers | head -3

Sorting by elapsed time picks out whatever started earliest, which on a healthy machine is the boot sequence and on an unhealthy one is often the thing that has hung.

Show output

Your output will differ: the elapsed times depend on how long your machine has been up

systemd               00:01
systemd-journal       00:01
cron                  00:00

Find which command has the most processes

ps -u user -o comm= --sort=comm | uniq -c | sort -rn

Sorting by name first is what makes uniq -c able to count, since it only collapses adjacent lines. The second sort puts the largest count first.

Show output

Your output will differ: the commands and counts depend on what your machine is running

      2 tips-backup
      1 tips-supervisor

Group processes by state

ps -e -o stat=,comm= --sort=stat | head -4

Sorting by state brings anything unusual together. R sorts first, and a run of Z entries in this listing means processes whose parent has not collected them.

Show output

Your output will differ: which processes are in which state depends on your machine

R    ps
Ss   systemd
Ss   systemd-journal
Ss   cron

List the daemons in reverse name order

ps --ppid 1 -o comm= --sort=-comm | uniq

A leading - reverses the sort, on a name as much as on a number. --sort orders the listing itself, so uniq collapses the repeats without a separate sort step. On a machine with virtual consoles the repeats are the agetty processes, one per console.

Show output
tips-supervisor
systemd-journal
sshd
cron

Order threads by count

ps -e -o comm=,nlwp= --sort=-nlwp | head -3

nlwp is the thread count. On a desktop this finds the browser; here everything is single-threaded, so the sort falls back to PID order.

Show output

Your output will differ: the thread counts depend on what your machine is running

systemd            1
systemd-journal    1
cron               1

Parents and the process tree

Every process except PID 1 has a parent, and a process whose parent exits is adopted by PID 1 rather than being killed. That is why a daemon started from a login shell survives the logout.

Show a process tree

ps -u user -o pid,ppid,comm --forest --no-headers

--forest draws the parent and child relationships with ASCII art. The supervisor's own parent is PID 1, because the shell that started it has already exited.

Show output

Your output will differ: the PIDs are specific to your machine

    747       1 tips-supervisor
    751     747  \_ tips-backup
    752     747  \_ tips-backup

Show the same processes without the tree

ps -u user -o pid,ppid,comm --no-headers

The same information as the previous example with the relationship left for you to read out of the PPID column. --forest is doing presentation, not selection.

Show output

Your output will differ: the PIDs are specific to your machine

    747       1 tips-supervisor
    751     747 tips-backup
    752     747 tips-backup

Find a process's parent

ps -o comm=,ppid= -C tips-backup | sort -u

The copies share a parent, so sort -u reduces the answer to one line. Following ppid upwards is how you find out what actually started something.

Show output

Your output will differ: the parent PID is specific to your machine

tips-backup         747

Indent a listing by hierarchy

ps -H -u user -o comm=

-H is the UNIX-syntax equivalent of --forest, indenting children without drawing the connectors.

Show output
tips-supervisor
  tips-backup
  tips-backup

pgrep and pidof

pgrep answers the question ps | grep is usually being asked, and it never matches itself. It matches against comm by default, and against the full command line with -f.

Count the processes matching a name

pgrep -c tips-backup

-c prints only the count. The exit status is non-zero when nothing matches, so this works as a test as well as a number.

Show output
2

List the PIDs matching a name

pgrep tips-backup | wc -l

Bare pgrep prints one PID per line. The pattern is matched against comm, so the pipeline running the command cannot match it.

Show output
2

Show names alongside the PIDs

pgrep -l tips

-l adds the process name. The pattern is a substring by default, so tips matches both tips-backup and tips-supervisor.

Show output

Your output will differ: the PIDs are specific to your machine

747 tips-supervisor
751 tips-backup
752 tips-backup

Show the full command line alongside the PIDs

pgrep -a tips-backup

-a prints the whole command line, which is what you want before killing anything, because it shows which of several similar processes you have found.

Show output

Your output will differ: the PIDs are specific to your machine

751 /usr/local/bin/tips-backup 3600
752 /usr/local/bin/tips-backup 3600

Require an exact name rather than a substring

pgrep -x tips; echo "exit $?"

-x demands that the whole name match, so the substring tips now matches nothing and the exit status is 1. Without -x the same pattern matches three processes.

Show output
exit 1

Confirm pgrep never matches itself

pgrep -c pgrep; echo "exit $?"

pgrep excludes its own process, which is the whole reason to prefer it to ps | grep. The count is 0 and the exit status says nothing matched.

Show output
0
exit 1

Select a user's processes by name

pgrep -u user -l .

-u narrows the search to one user and . is a pattern matching any name. -l adds the name beside each PID, and the list arrives oldest first, since pgrep walks /proc in PID order.

Show output

Your output will differ: the PIDs are specific to your machine

747 tips-supervisor
751 tips-backup
752 tips-backup

Produce PIDs on one line for another command

pgrep -x -d, tips-backup

-d sets the delimiter instead of a newline. A comma-separated list is what ps -p and renice expect.

Show output

Your output will differ: the PIDs are specific to your machine

751,752

Find the newest and oldest process of a name

pgrep -n tips-backup; pgrep -o tips-backup

-n is the most recently started match and -o the oldest. Useful when several copies exist and only one of them is the one you just started.

Show output

Your output will differ: the PIDs are specific to your machine

752
751

Count what pidof found

pidof tips-backup | tr " " "\n" | wc -l

pidof puts every PID on one line, so counting them means splitting on spaces first with tr.

Show output
2

Search a process's full command line

pgrep -u user -f "bin/tips-backup" | wc -l

-f matches against the whole command line rather than just comm, which is the only way to find a process whose name is its interpreter. It is also the option that matches the shell running your script, since that shell's command line contains the pattern; narrowing by -u is one way to exclude it.

Show output
2

Filter a listing without matching the filter

ps aux | grep "[t]ips-supervisor"

The brackets make the pattern a character class matching one literal t, so the text [t]ips-supervisor in grep's own command line no longer matches it. pgrep is clearer, but this trick is worth recognising in other people's scripts.

Show output

Your output will differ: the PID, the start time and the memory figures are specific to your machine

user         747  0.0  0.0   3912  2968 ?        Ss   10:41   0:00 /bin/bash /usr/local/bin/tips-supervisor

Pass the PIDs of a process to another command

ps -o pid= -C tips-backup | xargs echo

-o pid= produces bare numbers with no header to strip, which is what makes it safe to pipe into xargs. Substitute the command you actually want for echo once the list looks right.

Show output

Your output will differ: the PIDs are specific to your machine

751 752