pgrep Command: Tutorial & Examples
Find a process by name, then preview what you'd kill before you kill it.
What It Is
pgrep is the find-by-name shortcut for processes. It walks the kernel's live process table in /proc, matches the names against your pattern, and prints the PIDs. One job, done well. If you've read the ps page you've already heard the promise: pgrep is the shortcut so you stop typing grep -v grep for the rest of your life. This page is about cashing that promise in.
But there's a second, bigger reason pgrep is worth a full page: it has an identical-twin sister called pkill that takes the same flags and same patterns — except instead of printing the PIDs, it signals them. That symmetry is the whole game. You use pgrep to preview, then swap one word for pkill to execute. It is the single most useful safety habit in shell work, and most people never learn it.
Your First Look
Find every sshd process on the box:
pgrep sshd
892
14021
14025
Three PIDs, one per line, nothing else. That's the whole output by default — no headers, no command lines, no decoration — because pgrep is designed to be fed straight into another command (kill $(pgrep sshd), pgrep nginx | xargs ...). When you actually want to read it with your eyes, ask for more:
pgrep -a sshd
892 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
14021 sshd: holger [priv]
14025 sshd: holger@pts/0
That -a (list-full) is the version you'll type interactively 90% of the time — same matches, with the full command line glued on so you can actually tell which sshd is the daemon and which one is your own login session.
How I Use It
The shape of every pgrep session is the same three steps, and once you internalize them you'll never grep -v grep again.
Step 1: preview with pgrep -af. I almost never run pkill without first running the exact same command with pgrep instead — same flags, same pattern. If pgrep -af 'java.*MyApp' shows me the three PIDs and command lines I expect, then pkill -f 'java.*MyApp' is safe. If it shows me seven PIDs — including some I didn't expect — I caught the mistake before it cost me an outage. This preview-then-execute habit is the magic. It works because pgrep and pkill are literally the same binary in disguise, with identical flags.
Step 2: tighten the pattern. The default match is a substring against the process name only, capped at the famous 15 characters the kernel stores in /proc/<PID>/stat — see Gotchas. The two flags that fix almost every "I matched the wrong thing" problem are -x (exact match, no substring) and -f (match the full command line, not the truncated name). pgrep ssh matches sshd and ssh-agent and your ssh sessions all at once; pgrep -x ssh matches only the literal command name ssh; pgrep -f 'ssh holger@' matches a specific session you opened.
Step 3: hand the PIDs to the next tool. Either pipe through xargs, or use pgrep -d, to get a comma-joined list ready to drop straight into ps -p, renice, top -p, or anything else that takes a PID list. That's the pgrep loop: find → preview → act.
Pro Tip
the canonical
pgrep+xargsone-liner ispgrep -f 'pattern' | xargs -r kill -HUP— the-r(from GNUxargs) means "do nothing ifpgrepfound nothing," which keeps you from accidentally runningkill -HUPwith no arguments when the match misses. Tiny flag, saves real outages.
The Flags, Explained
Every pgrep flag, grouped by what it actually does for you.
Matching: how the pattern is compared.
-f— match against the full command line (/proc/<PID>/cmdline) instead of just the truncated 15-character name. This is the flag that unlocks targeting one specific Java app out of fourteenjavaprocesses. The pattern is an extended regex, sopgrep -f 'java.*MyApp'works as you'd hope.-x— exact match, no substring.pgrep -x sshmatches the literal namesshand nothing else.-i— case-insensitive match.-v— invert the match, list everything that doesn't match.
Filtering: AND-narrow the candidate set. Every filter you add must match for a process to be listed — pgrep -u root sshd is "named sshd AND owned by root."
-u euid/-U uid— by effective or real user ID.pgrep -u www-datalists everything your web user owns.-g pgid/-G gid— by process group or real group ID.-P ppid— by parent PID. The killer feature; see Reading by Example.-s sid— by session ID.-t tty— by controlling terminal (e.g.-t pts/0).-r state— by run state:D,R,S,Z,T,I.pgrep -r Zto find all your zombies.-O secs— only processes older than N seconds. Lovely for "find the server that's been running more than a day."-n/-o— keep only the newest or oldest match.-A— ignorepgrep's own ancestors (shells,sudo, etc.), useful when you accidentally match your own login chain.--cgroup name/--ns pid— modernsystemdworld: filter by cgroup v2 or by sharing a namespace with another PID. The bridge from old-school process matching to the container era.-F file— read PIDs from a pidfile. Made for daemons and-L(fail if the pidfile isn't locked).
Output: how the result is printed.
-l— list PID and (short) process name.-a— list PID and full command line. The interactive default.-c— count only, no PIDs. Exit code is non-zero on a count of 0, which makes it scriptable.-d ','— change the delimiter from newline to whatever you want. The classic-d,gives you a comma-joined list ready fortop -porps -p.-w— show every thread ID instead of the PID. Surprises you when Java shows up as fifty rows.
Warning
pgrep sshmatchessshd,ssh-agent, your interactivesshsessions, and thesshkeepalivesystemdran at boot — all at once. Feed that topkillon a remote box and you've just kicked yourself out, killed the agent holding your keys, and stopped the daemon. Usepgrep -x sshdfor the daemon,pgrep -af sshto see what you'd hit, and the preview habit above to never get bitten.
Reading It by Example
The handful of pgrep recipes worth real muscle memory.
Preview, then kill — the safety pattern:
pgrep -af 'java.*MyApp' # what would I hit?
pkill -f 'java.*MyApp' # same flags, same pattern, now do it
The two lines differ by exactly one letter. Read line 1; if it's right, run line 2. Every time.
Every child of one process, and the whole tree gone in one line:
pgrep -P 14021 # all children of PID 14021
pkill -P 14021 # ...and kill them all in one shot
The -P flag matches by parent PID and it is genuinely beautiful for cleaning up a misbehaving worker pool, a runaway build, or a shell that spawned thirty subprocesses. Combine with -d, and you get a comma-joined PID list ready for top -p to watch the same tree:
top -p "$(pgrep -d, -P 14021)"
The "feed top a service" trick (the same line that appears on the top page):
top -p "$(pgrep -d, nginx)"
Every nginx worker, watched live in one top window. The -d, is non-negotiable here — top -p wants a comma-joined list.
Is the service up? (the cron-friendly check):
if ! pgrep -x mysqld > /dev/null; then
echo "mysqld is down" | mail -s "alert" admin@example.com
fi
Exit code 0 if at least one match, non-zero if none — pure POSIX plumbing.
Tell the daemon to reload its config without restarting it:
pkill -HUP nginx
SIGHUP is the universal "reread your config file" signal across well-behaved Unix daemons — far gentler than a full restart. (Preview first: pgrep -af nginx.)
Find the zombies:
pgrep -r Z -a
Filters by run state. A handful of zombies is fine; piles of them sharing one parent point to a bug in that parent — see the ps Reading-by-Example for the diagnosis.
Cheat Sheet
The lines worth memorizing:
pgrep -af 'pattern'— preview what you'd kill, with full command lines. The single most useful invocation.pgrep -x name— exact name match, no substring surprises.pgrep -f 'java.*MyApp'— match the full command line with a regex.pgrep -u www-data— every process owned by a user.pgrep -P $PPID— every child of a parent PID.pgrep -d, name— comma-joined list, ready fortop -porps -p.pgrep -c name— just the count.pgrep -n name/-o name— newest / oldest match only.pkill <same flags>— once you knowpgrep, you knowpkill.
How You'll Actually Use It
Three places in real life. One: every time you're about to kill something by name, you type pgrep -af first, read it, then swap to pkill. Two: in shell scripts and cron jobs as the "is this daemon alive?" gate (if ! pgrep -x foo). Three: to build the PID list for another tool — top -p "$(pgrep -d, ...)", renice 10 $(pgrep ...), strace -p $(pgrep -n ...) to attach to the newest match. If you find yourself piping ps aux | grep anywhere in a script, that's the moment you reach for pgrep instead — shorter, faster, and it doesn't accidentally match the grep itself.
Worth saying out loud: on a systemd box, the right way to manage a known service is systemctl restart nginx or systemctl kill --signal=HUP nginx — systemd knows the cgroup so it can't miss a child or hit the wrong process. pgrep and pkill are for everything that isn't a managed service: your own scripts, ad-hoc workers, the build that wedged, the Python repl you left running on the server.
Gotchas
- The process name is truncated to 15 characters. This is the rule that bites everyone exactly once. The kernel stores only 15 chars in
/proc/<PID>/stat, sopgrep my-very-long-daemonmatches nothing — butpgrep -f my-very-long-daemon(full command line) works perfectly. When in doubt, use-f. - Substring matching is the default, and it's promiscuous.
pgrep sshmatchessshd,ssh-agent, and every livesshsession. Use-xfor exact, or-fplus a tighter regex. - The pattern is an extended regex, not a glob.
pgrep 'nginx|apache'works;pgrep 'nginx*'does not mean what you think. -fmatches anything on the command line — including the arguments topgrepitself. Mostly handled (it ignores its own PID), but abashscript that contains the pattern in its source can match itself. Add-Ato ignore your own shell chain.- No privileges, no visibility. Without root you only see processes you can already see in
ps— usually your own and globally visible ones. On hardened boxes withhidepidmounted on/proc, even less. - PIDs get reused. A PID you captured ten minutes ago may now belong to a completely different program. For anything beyond a quick interactive kill, re-match right before you act.
History & Philosophy
pgrep and pkill came from Solaris in 1996 and were ported into the Linux procps package soon after — late arrivals to the UNIX command set, written specifically to retire the ps | grep | awk | kill pipeline that everybody was typing wrong. They're built as one binary with two names: the executable inspects argv[0] to decide whether it's pgrep (print) or pkill (signal). Same code, same flags, same matching logic — that's why they behave identically, and it's why the preview-then-execute habit is so cheap to teach.
The deeper magic is the same one that runs through top and ps: pgrep isn't doing anything you can't do yourself. It opens /proc, iterates over the numeric directories, reads each /proc/<PID>/stat for the name (or cmdline when you pass -f), and matches. That's it. The whole "find a process by name" feature is twenty lines of shell over a /proc walk — but having it as a battle-tested binary with the same flags as pkill is what makes it safe, and safety is the actual feature.
Flag Reference
| Flag | Meaning |
|---|---|
-f |
Match against the full command line, not just the process name |
-l |
List the process name alongside the PID |
-a |
List the full command line alongside the PID |
-n |
Keep only the newest match |
-o |
Keep only the oldest match |
-u USER |
Match processes by their owner |
-x |
Exact match — no substring matching |
-c |
Print only the count of matches |
-d DELIM |
Set the output delimiter between PIDs |
-v |
Invert the match — list processes that do not match |
See Also
- pkill — the sister command; identical flags, signals instead of prints
- ps — the full process snapshot
pgrepis a shortcut over - kill — what you do once you have the PID
- killall — alternative match-by-name; less precise, simpler
- xargs — the pipe target that turns PIDs into actions
- top / htop — pair with
-p "$(pgrep -d, ...)"to watch a service - systemd — the modern way to manage a known daemon, cgroup-aware
- process / /proc — what
pgrepis actually reading
Tired of running
pgrepby hand to check whether your services are still alive?CleverUptime watches every process on every server, every minute — it already knows the PIDs, the parents, the cgroups, and which one died — and tells you in plain language which service stopped and what spawned the zombie pile, before you'd think to type
pgrep.Want to see your own server's health right now? One command, no signup, no install.