pkill Command: Tutorial & Examples

Finds running processes by name or attribute and sends them a signal, letting you kill a process without first looking up its numeric ID.

What It Is

pkill sends a signal to a process you pick out by name or pattern rather than by number. Where kill wants a PIDkill 4815pkill wants a description: pkill nginx means "find every process whose name looks like nginx, and signal all of them." You never have to look the number up first. That's the whole pitch, and it's a good one: most of the time you know what you want to stop (nginx, that runaway python script, every process a logged-out user left behind) long before you know its PID.

pgrep is the same idea with the trigger taken off. It runs the exact same search — same patterns, same flags — but instead of signalling anything it just prints the PIDs it found. Nothing dies. That makes it the most useful command on this page, because it lets you look before you leap: run pgrep to see precisely who matches, read the list, and only then run pkill to act on it. The two are so close they are, quite literally, the same program (more on that at the end). Think of them as a matched pair — pgrep asks the question, pkill answers it with a signal.

This page is about that act of matching by name, and the trap hiding inside it. Naming a process is fuzzier than pointing at a number, and "fuzzy" on a server is how you end up signalling six things when you meant one. We'll get the matching exactly right, and we'll spend real time on the single flag — -f — that makes pkill powerful and dangerous in the same keystroke. (For what a signal actually is and the polite-then-forceful SIGTERM-before-SIGKILL discipline, see signal; for the by-the-number primitive underneath all of this, kill.)

Your First Look

Start with the harmless one. Ask who's running, by name:

pgrep -a sshd
3101733 sshd: /usr/sbin/sshd -D [listener]
1218415 sshd-session: admin [priv]
1224256 sshd-session: admin@pts/0

-a (list-full) tells pgrep to print the full command line next to each PID, not just the bare number — so you can actually read what matched. Here it's the listening daemon itself plus the login session you're sitting in. That output is your safety check. Now, and only now, the loaded version:

pkill sshd

Silence. On Unix silence means success — every matching process got SIGTERM, the default, the polite "please wrap up." If nothing matched, pkill is also silent, but it exits with status 1, so a script can still tell the difference. Want a spoken receipt instead of silence? Add -e (echo):

pkill -e nginx

With -e, pkill prints a line for each process it actually signalled — lines like nginx killed (pid 4821), one per match — so you get a receipt rather than guessing whether anything happened.

That's the whole rhythm of this page in three lines: ask with pgrep, read the answer, act with pkill.

pgrep -a nginx          # who matches "nginx"? (look — nothing dies)
pkill nginx             # signal them all with SIGTERM
pkill -9 stuck-thing    # SIGKILL — only after SIGTERM was ignored

How I Match Safely

The whole craft of pkill is in the matching, because a name is a vaguer thing to aim with than a number, and the default aim is wider than people expect. Here is the exact sequence I run, every time, and the two questions behind it.

First question: am I sure who this hits? Then prove it with pgrep. I almost never type pkill cold. I type the search with pgrep -a first — same pattern, same flags — read the list of PIDs and command lines, and confirm it's only what I meant. Then I press up-arrow and change the one word: pgrep -a becomes pkill. If the preview shows three processes I expected, great. If it shows seven, two of them things I'd never have guessed, I just caught a mistake while I still had a server to catch it on. That one-word swap is the cheapest insurance in all of shell work, and it's only that cheap because the two commands take identical arguments.

Second question: is the default match too loose, and do I need to tighten it? By default pkill does a substring match against the process name — and "substring" is the word that bites. pkill ssh does not mean "the program called ssh." It means "anything with ssh anywhere in its name." Watch what that really catches on a live server:

pgrep -a ssh
  76473 /usr/bin/ssh -N -n -L 3306:localhost:3306 db.example.com
  80272 /usr/bin/ssh -N -n -L 5432:localhost:5432 db.example.com
1218415 sshd-session: admin [priv]
1224256 sshd-session: admin@pts/0
3101733 sshd: /usr/sbin/sshd -D [listener]

Every one of those lines matches ssh. So pkill ssh would tear down both database tunnels, kill the sshd daemon that accepts your next login, and kill sshd-session: admin@pts/0 — the very session your fingers are inside right now. The connection dies mid-word and you can't get back in, because the thing that answers the door is the thing you just shot.

And the pattern itself is fussier than it looks: it's an extended regular expression, not a shell glob. pkill 'nginx|apache' matches either — that's regex alternation — but pkill 'nginx*' does not mean "starts with nginx"; in regex * means "zero or more of the previous character," so that pattern reads as "ngin, then any number of x's," almost never what you want. When you mean "contains," just write the plain word — the match is a substring search by default, not an anchor, so pkill foo already catches foobar and barfoo. (-i makes that search case-insensitive, the same -i you know from grep.)

Two flags fix nearly every too-loose match:

  • -x (exact) — match the name exactly, no substring, the regex anchored end to end for you. pkill -x ssh hits only a process literally named ssh, and leaves sshd and the rest alone. (You can also anchor by hand with '^ssh$'.)
  • -f (full) — match against the entire command line, not just the name. This is how you single out one java process among fourteen by what comes after java: pkill -f 'java.*MyApp'. It is also, as we're about to see, the flag most likely to take your hand off.

Warning

pkill ssh on a remote box is the classic self-inflicted lockout. Substring match grabs the daemon, your tunnels, and your own live session — the connection drops mid-keystroke and sshd is gone, so you can't reconnect. Preview with pgrep -a ssh first, every time, or say exactly what you mean with pkill -x sshd.

The -f Footgun

-f is the flag worth slowing down for, because the same property that makes it powerful is the one that makes it dangerous.

Normally pkill only sees a process's name — and not even all of it. The kernel keeps just the first 15 characters of the name in /proc/<PID>/stat, a limit set decades ago and never raised, so pkill my-very-long-daemon matches nothing at all — the kernel only ever knew it as my-very-long-da. The first time that happens you'll swear the command is broken. It isn't; it's reading a name that was trimmed before it ever got there.

-f lifts that ceiling by matching against the full command line — every argument, the whole string the process was launched with, read live from /proc/<PID>/cmdline. Suddenly you can target anything: the long daemon name, a specific script, one worker out of a pool, by any word that appears anywhere on its invocation.

And there's the catch, sitting in plain sight: anywhere on its invocation. -f matches every program whose command line merely contains your pattern — and the program most likely to contain your target's name on its command line is the editor you have that very file open in.

Picture it. You're tidying up a wedged deploy and you reach for the obvious:

pkill -f deploy.sh

That pattern matches the running deploy.sh, yes. It also matches vim deploy.sh in the other terminal — your editor, whose command line is the literal string vim deploy.sh, the filename and all — and SIGTERM lands there too. Your unsaved edits, and the only witness to the crime, gone in the same breath. The command did exactly what you said; you just didn't realise you'd described your editor as well. A pattern like tail, or log, or a bare project name, will quietly sweep up the less paging it, the grep hunting through it, and the shell whose history mentions it.

The defence is the habit from the last section, and -f is precisely where it earns its keep: run the identical search with pgrep first and read every line it prints.

pgrep -fa deploy.sh

If vim deploy.sh shows up in that list, you just saw it before the kernel did. Tighten the pattern (pkill -f '/usr/bin/deploy.sh', or anchor it with a regex), or add -A to make pkill ignore its own ancestor processes — handy when a script would otherwise match its own command line.

Danger

pkill -f <pattern> matches the pattern against the whole command line of every process — which includes editors, pagers, greps, and shells that merely have the target's name on their command line. A broad -f pattern run as root can take down far more than you named. Preview the identical command with pgrep -fa first, read it line by line, and only then swap pgrep -fapkill -f.

Matching by Something Other Than Name

Name is the headline, but you can pin down a process by almost any attribute it has, and these combine — every filter you add must also be true (a logical AND). pkill -u root sshd means "named sshd and owned by root." The most useful ones:

  • -u user / -U user — by effective or real owner. pkill -u alice with no name at all signals every process alice owns — the "force-logout" move.
  • -P ppid — only children of a given parent PID. pkill -P 14021 cleanly stops a whole subtree — a build shell that spawned thirty helpers, a runaway worker pool — without touching the parent.
  • -t pts/3 — by controlling terminal. "Kill everything still clinging to that dead session."
  • -O secs (older) — only processes that have been alive longer than N seconds. The basis of a "reap anything that's been stuck for an hour" cron sweep.
  • -n / -o — keep only the newest or oldest of the matches. pkill -o -f 'python worker' kills just the longest-lived one.
  • --cgroup name — match a cgroup, the bridge from old-school name matching into the systemd and container world.

When none of these is precise enough — or you want to hand the matches to a tool that isn't pkill — that's the real division of labour: pgrep finds, and you pipe its PIDs onward (renice $(pgrep chrome), top -p "$(pgrep -d',' nginx)").

Picking the Signal

By default pkill sends SIGTERM, and that's almost always the right choice — it asks a program to shut down cleanly and gives it the chance to finish writing files and close connections. You override it by naming another signal as a flag:

  • -HUP — to a daemon, "reread your config file." pkill -HUP nginx reloads nginx without a restart; the same convention holds across most well-behaved daemons.
  • -STOP / -CONT — freeze a process in place (zero CPU, no state lost) and later thaw it. The debugging pause button.
  • -9 (SIGKILL) — the kernel removes the process with no chance to clean up. This is the last resort, only after SIGTERM has been ignored, never the first reach. The full catalogue and the why-TERM-before-KILL discipline live on the signal and kill pages.

One honest limit: a process stuck in D state (uninterruptible sleep — wedged inside a kernel call, usually a frozen disk or dead NFS mount) cannot be signalled at all, not even by -9, until its I/O returns. The signal you send just waits in the queue; the process never surfaces from the kernel to notice it, so it sits there untouchable until the read it's blocked on finally answers — and there's nothing pkill or kill can do to hurry that along. The whole story is on the process page.

Reading It by Example

The recipes worth committing to muscle memory.

The one-word swap — the only habit that matters:

pgrep -fa 'java.*MyApp'      # what would I hit? read every line.
pkill -f  'java.*MyApp'      # same pattern, same flags — now act.

Reload a daemon's config without restarting it:

pkill -HUP nginx

Stop a whole process subtree by its parent:

pkill -P 14021               # every direct child of PID 14021

Force every process belonging to one user to quit:

pkill -u alice

Freeze, inspect, resume — without losing any state:

pkill -STOP -f 'python worker'   # pause it
# ...attach a debugger, peek at /proc, look around...
pkill -CONT -f 'python worker'   # let it run again

Cheat Sheet

The lines worth memorising:

  • pgrep -a pattern (look)pkill pattern (act) — the safe loop. The whole page in two lines.
  • pgrep -fa pattern (look)pkill -f pattern (act) — same, for full-command-line matching. Always preview -f.
  • pkill -x exact-name — exact match, no substring lockouts.
  • pkill -HUP daemon — reload config without a restart.
  • pkill -u user — every process owned by USER (force-logout).
  • pkill -P ppid — every child of one parent.
  • pkill -e ... — echo what was signalled (the receipt).
  • pkill -9 thingSIGKILL, last resort, only after SIGTERM was ignored.

Gotchas

  • The default match is a substring, and that's promiscuous. pkill ssh grabs sshd, your tunnels, and your own live session. Use -x for exact, -f plus a tight pattern, or always preview with pgrep.
  • The 15-character name limit silently swallows long names. pkill my-very-long-daemon matches nothing; the kernel only stored my-very-long-da. Use -f for anything longer than that.
  • -f matches the whole command line of everything — editors, pagers, greps, your own shell. The widest footgun on the page. Preview, always.
  • The pattern is a regex, not a glob. pkill 'nginx*' is a regex meaning "ngin and any number of x's," not "starts with nginx." Write the plain word for "contains."
  • pkill -v is deliberately crippled. On pgrep, -v inverts the match — safe, you're only printing. On pkill it would mean "signal everything that doesn't match," so the short flag is disabled; only the spelled-out --inverse reaches it, on purpose.
  • You can only signal what you own. Mortals can't signal root's processes; root can signal anyone — which is also why a broad pkill as root is so much more dangerous.
  • Exit code 1 means "no matches" or "couldn't signal" — they look identical to a script. If you need to be sure, count first with pgrep -c.
  • A D-state process won't die. Not even -9 reaches it until its stuck I/O returns.

History & Philosophy

Before pgrep existed, signalling a process by name meant building a fragile little pipeline by hand: list every process with ps, filter it with grep, carve the PID out with awk, and feed that to kill — and the grep half had its own famous trap, because ps | grep nginx always matched the grep nginx you'd just launched (the workaround, grep '[n]ginx', is a piece of folklore in its own right). The pair pgrep/pkill were written for Sun's Solaris 7 in 1998, by Mike Shapiro, expressly to retire that error-prone pipeline and give the operation a clean, single name. POSIX and the Linux procps package adopted them, and they're in every distro today.

Here's the fact that makes the whole pair click, and it's a small marvel: pgrep and pkill are the same binary. One executable, two names hard-linked to it on disk. When it starts, it looks at argv[0] — the name it was called by — and if that name was pkill it signals what it finds, and if it was pgrep it merely prints. (There's a third name, pidwait, that blocks until a match exits.) Same code, same matcher, same flags — which is exactly why the preview-then-act habit costs you only one changed word: you're running one program two ways, and it can't disagree with itself about who matches. A tool whose only job is identifying processes by the name they go by, that decides what it itself is by reading the name it was called by. The whole thing is just a walk through the numbered directories of /proc, reading a name out of each one and comparing it to yours — the same /proc walk underneath top, ps, and every process tool on the box. pkill only adds the flag vocabulary and the loaded trigger; pgrep is that same tool, kindly, with the trigger removed.

See Also

  • pgrep — this page's other half: identical search, prints PIDs instead of signalling. Always look here first.
  • kill — the by-the-number primitive; one PID, one signal, and the full signal catalogue.
  • killall — the older kill-by-name; exact-match by default, no preview sibling, and a BSD-vs-Linux name-collision history.
  • ps — the full process snapshot the matching walks over.
  • top / htop — interactive, with a built-in key to send signals.
  • systemctl — the right tool when the target is a systemd-managed service.
  • signal — what you're actually sending, and the SIGTERM-before-SIGKILL discipline.
  • process / PID / /proc — the concepts underneath the name.

Not sure what a pkill pattern would really take down before you press enter?

CleverUptime keeps a live picture of every process and its parent on each server you run, so you can see exactly who matches a name before you signal it — and the moment a daemon you just signalled fails to come back, the alert lands in your inbox.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →