ps
Show a snapshot of running processes
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
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
Count the processes belonging to one command
ps -o comm= -C tips-backup | wc -l
Piping a headerless listing into wc -l is the general way to count matches. pgrep -c does the same job in one command.
Show output
2
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.
Print just the name of a process
ps -o comm= -p 1
The smallest useful -o: one field, no header, one line of output.
Show output
systemd
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
Check the exit status when nothing matches
pgrep nosuchprocess; echo "exit $?"
Nothing on stdout and exit 1. That makes if pgrep -x sshd >/dev/null; then a reliable test, and it is the reason exit codes matter here.
Show output
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
Get the PID of a single well-known process
pidof systemd
pidof is the older, simpler tool: exact name match, all PIDs on one space-separated line. pidof -s stops at the first.
Show output
1
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