kill

Send a signal to a process by id or by name

Updated 2026-08-29

kill sends a signal to a process. It usually ends one because the default signal is TERM, which asks a program to shut down and most programs agree to. The same command sends STOP, CONT and HUP, none of which ends anything. Processes and signals covers what the signals mean and what a program is allowed to do about each of them.

Naming the target

kill takes process ids and nothing else, so kill firefox is an error rather than a search. Three commands close that gap and they select differently.

pkill treats its pattern as an extended regular expression and matches it against the process name, or against the whole command line under -f. killall matches a name exactly unless you give it -r. pgrep is pkill with no signal attached: the same selection, printed instead of acted on.

Run the pgrep before the pkill. A pattern that turns out to match four processes rather than one is a great deal cheaper to discover that way.

In an interactive shell kill also takes a job spec, so kill %1 signals the first thing you backgrounded. Job control covers those, and they are the one form that reaches a whole pipeline rather than a single process.

Why -9 is a last resort

kill -9 sends SIGKILL, and the kernel removes the process without delivering anything to it. Nothing runs on the way out: no flush, no lock file removed, no child signalled. A database that was mid-write is a database that stays mid-write.

TERM first, then. -9 is for the program that has already been asked and has not gone, which in practice means one that installed a handler and ignores or mishandles the request.

A process stuck in uninterruptible sleep is the case where -9 looks broken. It is waiting on a kernel call that cannot be interrupted, usually storage or a hung network mount, and the signal sits pending until that call returns. The ps state column shows D for it, and nothing you send will change the situation.

Two kills and two packages

kill is a bash builtin, and /usr/bin/kill from procps is a separate program with different options and different error messages. At a prompt you get the builtin. In a script run through sh, or under sudo, or from find -exec, you get the binary.

pkill and pgrep come from procps, which Debian marks important; killall comes from psmisc, which is optional. A container or a debootstrap install can easily have the first pair and not the second.

Sample files used on this page

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

a small process tree the page needs something to signal, so the setup script starts five processes. tips-supervisor runs two copies of tips-worker and waits for them; it is a session leader, so those three share one process group. tips-stubborn ignores TERM and tips-graceful handles it. The examples run as root, so -u user names the supervisor's tree and nothing else.

root     tips-graceful   /bin/bash /usr/local/bin/tips-graceful
root     tips-stubborn   /bin/bash /usr/local/bin/tips-stubborn
user     tips-supervisor /bin/bash /usr/local/bin/tips-supervisor
user     tips-worker     /usr/local/bin/tips-worker 3600
user     tips-worker     /usr/local/bin/tips-worker 3600
53 outputs, collapsed by default

Sending a signal

kill takes process ids. The examples below find them with pgrep first, because a page cannot print a PID that will still be there when you read it.

Ask a process to shut down

kill $(pgrep -x tips-worker); sleep 0.5; pgrep -x -c tips-worker

With no signal named, kill sends TERM. Both workers take it and go, which the pgrep -c afterwards confirms by counting none. The sleep is there because kill returns as soon as the signal is queued, not when the process has finished acting on it.

Show output
0

Name the signal instead of taking the default

kill -TERM $(pgrep -x tips-worker); sleep 0.5; pgrep -x -c tips-worker

The same thing said out loud. -TERM, -SIGTERM, -15 and -s TERM are four spellings of one signal, and a script is easier to read for using a name.

Show output
0

Send a signal by number

kill -15 $(pgrep -x tips-worker); sleep 0.5; pgrep -x -c tips-worker

Numbers work, and older scripts are full of them, so they are worth being able to read. Above signal 31 they stop being portable between architectures, so prefer the name when you are the one writing.

Show output
0

Use the long form -s

kill -s KILL $(pgrep -x tips-stubborn); sleep 0.3; pgrep -x -c tips-stubborn

-s takes the signal as a separate argument. Use it when the signal comes from a variable: kill -s "$sig" "$pid" needs no assembly.

Show output
0

Signal several processes at once

kill $(pgrep -x tips-worker) && echo sent

kill accepts any number of PIDs and signals each in turn. pgrep prints one per line and the shell splits them into separate arguments.

Show output
sent

Test whether a PID exists

kill -0 "$(pgrep -x -n tips-worker)"; echo "exit=$?"

Signal 0 is not a signal. It runs the permission and existence checks and then sends nothing, so the exit status answers "is this process still there, and may I signal it?" without disturbing it. This is how a script waits for something to finish.

Show output
exit=0

See what -0 says about a PID that is gone

kill -0 999999 2>/dev/null; echo "exit=$?"

Status 1, and a message on stderr that the redirect discards. A script polling for a process to exit wants exactly this pair.

Show output
exit=1

Pipe PIDs into kill with xargs

pgrep -x tips-worker | xargs kill; sleep 0.5; pgrep -x -c tips-worker

Equivalent to the command substitution above, and the form to use when the list could be long enough to overflow a command line. xargs also stops you sending a signal to nothing when pgrep finds nothing, if you add -r.

Show output
0

Send HUP to a program with no handler for it

kill -HUP $(pgrep -x tips-worker | head -1); sleep 0.4; pgrep -x -c tips-worker

HUP is conventionally "reload your configuration", but that is a convention among daemons rather than a kernel behaviour. A program that installs no handler gets the default action, which for HUP is to die: one worker of the two is gone.

Show output
1

When TERM is not enough

A program can catch TERM and decide what to do, including nothing. KILL and STOP are the two signals it cannot catch, and they are handled by the kernel rather than by the process.

Watch a process ignore TERM

pkill -x tips-stubborn; sleep 1; pgrep -x -c tips-stubborn

tips-stubborn runs trap '' TERM, so the signal is delivered and discarded. A second later it is still there. Real programs reach this state by accident more often than on purpose, usually by trapping the signal to clean up and then blocking in the handler.

Show output
1

Fall back to KILL

pkill -9 -x tips-stubborn; sleep 0.5; pgrep -x -c tips-stubborn

-9 is SIGKILL, which the process never sees. The kernel tears it down where it stands, so nothing it was holding gets released in an orderly way and any temporary file it made stays made.

Show output
0

Spell the same thing with --signal

pkill --signal KILL -x tips-stubborn; sleep 0.3; pgrep -x -c tips-stubborn

pkill and pgrep accept GNU long options. --signal KILL reads better than -9 in a script somebody else will have to change.

Show output
0

Suspend a process

pid=$(pgrep -x -n tips-worker); kill -STOP "$pid"; sleep 0.2; ps -o stat= -p "$pid"

STOP cannot be caught either, and it does not end the process: it takes it off the run queue and leaves it there. The ps state column reports T, for stopped.

Show output
T

Let it run again

pid=$(pgrep -x -n tips-worker); kill -STOP "$pid"; sleep 0.2; ps -o stat= -p "$pid"; kill -CONT "$pid"; sleep 0.2; ps -o stat= -p "$pid"

CONT is what undoes it. Between the two the process holds every file, socket and lock it had. That makes STOP useful for pausing something expensive, and dangerous for pausing something holding a database connection.

Show output
T
S

Stop a process, then TERM it

pid=$(pgrep -x -n tips-worker); kill -STOP "$pid"; sleep 0.3; kill -TERM "$pid"; sleep 0.5; ps -o stat= -p "$pid"; pgrep -x -c tips-worker

Still T, and both workers still counted. A stopped process is off the run queue and executes nothing, so the TERM joins its pending set and stays there. A cleanup script that suspends processes first and terminates them second leaves the whole set stopped.

Show output
T
2

Continue it, and the waiting signal lands

pid=$(pgrep -x -n tips-worker); kill -STOP "$pid"; sleep 0.3; kill -TERM "$pid"; sleep 0.5; kill -CONT "$pid"; sleep 0.5; pgrep -x -c tips-worker

CONT puts it back on the run queue, where the first thing it does is act on the signal it was holding. The TERM never needed sending twice.

Show output
1

Try the same on a process that handles TERM

pid=$(pgrep -x tips-graceful); kill -STOP "$pid"; sleep 0.3; kill -TERM "$pid"; sleep 0.5; ps -o state= -p "$pid"; kill -CONT "$pid"; sleep 0.5; pgrep -x -c tips-graceful

tips-graceful traps TERM and exits from the handler. It behaves exactly as the worker did: T while stopped, gone once continued. Whether the signal would reach a handler or the kernel's own default action changes nothing, because either way the process has to be scheduled to get there.

Show output
T
0

End a stopped process without continuing it first

pid=$(pgrep -x -n tips-worker); kill -STOP "$pid"; sleep 0.3; kill -KILL "$pid"; sleep 0.5; pgrep -x -c tips-worker

KILL is applied by the kernel rather than by the process, so a stopped target is torn down where it lies. Continuing it first and sending TERM after is the politer route, at two more steps.

Show output
1

Selecting by name

pgrep and pkill take the same selection options and differ only in what they do with the result. Everything here works on either.

Count the processes a pattern matches

pgrep -x -c tips-worker

-c prints the count instead of the PIDs, and -x requires the whole process name to match rather than any part of it.

Show output
2

List the PIDs a pattern matches

pgrep -x tips-worker

The default output: one PID per line, ready for xargs or a command substitution.

Show output

Your output will differ: the PIDs are assigned at boot and will differ on your machine

2724
2725

Show the command line beside each PID

pgrep -a -x tips-worker

-a adds the full command line. Check the selection here before letting pkill act on the same pattern.

Show output

Your output will differ: the PIDs will differ

2724 /usr/local/bin/tips-worker 3600
2725 /usr/local/bin/tips-worker 3600

Show the process name beside each PID

pgrep -l -x tips-worker

-l is the shorter form of the same idea, printing the name rather than the whole command line.

Show output

Your output will differ: the PIDs will differ

2724 tips-worker
2725 tips-worker

See that a pattern matches on substrings by default

pgrep -c tips-work; pgrep -x -c tips-work

Without -x, the pattern is a regular expression matched anywhere in the process name, so tips-work finds tips-worker. With -x it has to match the whole name, and finds nothing. The second number is why -x belongs on anything that sends a signal.

Show output
2
0

Kill by exact name

pkill -x tips-worker; sleep 0.5; pgrep -x -c tips-worker

pkill with the same selection as the pgrep you just ran. The two share every selection option for exactly this.

Show output
0

Say which processes were signalled

pkill -e -x tips-worker

-e reports each process it acted on. Worth having in a script, where the alternative is a silent command and no record of what it hit.

Show output

Your output will differ: the PIDs will differ

tips-worker killed (pid 645)
tips-worker killed (pid 646)

Count without listing

pkill -c -x tips-worker

-c prints how many processes were signalled and nothing else.

Show output
2

Find nothing, and see the exit status

pkill -x nosuchproc; echo "exit=$?"

Status 1 for no match, and no output at all. Scripts that treat any non-zero status as an error will trip on this, so test for it rather than letting set -e end the run.

Show output
exit=1

Match against the whole command line

pgrep -c -u user -f 'tips-worker 3600'

-f matches the pattern against the full command line rather than the process name. Nothing else separates two copies of one program running with different arguments.

Show output
2

See what -f actually matched

pgrep -u user -f 'tips-worker 3600'

The same selection printed. Always run this before the pkill, because -f widens what a pattern can hit by a great deal.

Show output

Your output will differ: the PIDs will differ

1031
1032

Kill on a command-line match, narrowed by user

pkill -u user -f 'tips-worker 3600'; sleep 0.5; pgrep -c -x tips-worker

-u restricts the match to one owner. Pair it with -f as a habit: your own shell's command line contains the pattern you just typed, so an unnarrowed pkill -f can match the shell running it.

Show output
0

Select the newest or the oldest match

pgrep -x -n tips-worker; pgrep -x -o tips-worker

-n is the most recently started process matching the pattern and -o the oldest. Useful when a program is meant to be a singleton and two of it are running.

Show output

Your output will differ: the PIDs will differ, and so will which of the two is newer

2725
2724

Signal every process owned by a user

pkill -u user; sleep 0.5; pgrep -c -u user

With no pattern at all, the selection is whatever the other options say. This ends every process belonging to user, including their login shell, and belongs to deleting an account rather than to an ordinary day.

Show output
0

Signal the processes on a terminal

pkill -t pts/0 -x tips-worker

-t selects by controlling terminal. The fixture's processes have none, so nothing is printed and nothing is signalled. A daemon is detached from every terminal, and -t cannot reach one.

killall

killall is the psmisc equivalent and predates pkill. The difference to hold on to is that it matches names exactly by default, where pkill matches substrings.

Kill every process with a given name

killall tips-worker; sleep 0.5; pgrep -c -x tips-worker

No -x needed: killall matches the whole process name unless told otherwise.

Show output
0

Report what was signalled

killall -v tips-worker

-v names each process, its PID and the signal number, which is more than pkill -e prints.

Show output

Your output will differ: the PIDs will differ

Killed tips-worker(2359) with signal 15
Killed tips-worker(2361) with signal 15

Wait until the processes have actually gone

killall -w tips-worker; pgrep -x -c tips-worker

-w blocks until every signalled process has exited, so the count that follows needs no sleep to be right. pkill has no equivalent, and this is the reason to keep killall installed.

Show output
0

Send a signal other than TERM

killall -s STOP tips-worker; sleep 0.2; ps -o stat= -C tips-worker

-s takes a name or a number, the same as kill. Both workers are stopped rather than ended, so ps prints T twice.

Show output
T
T

Match names with a regular expression

killall -r "tips-.*"; sleep 0.5; pgrep -c "tips-"

-r turns the argument into a regular expression, which is pkill's default behaviour rather than an option. All five fixture processes are signalled and one survives, because tips-stubborn ignores TERM.

Show output
1

Restrict to one owner

killall -u user tips-worker; sleep 0.5; pgrep -x -c tips-worker

-u is the same idea as pkill -u, and the same advice applies: narrow before you widen.

Show output
0

Find nothing, and be told so

killall nosuchproc; echo "exit=$?"

killall prints a message where pkill says nothing. Both exit 1.

Show output
nosuchproc: no process found
exit=1

Confirm before each one

killall -i tips-worker

-i asks about every match and reads the answer from the terminal, so it does nothing useful in a script or a pipeline. There is no output block here because the command waits for a keypress that the replay has no way to send.

Children, groups and orphans

A signal goes to one process. Its children are separate processes and are not included, so stopping a program and stopping what it started are two different acts.

Kill a parent and watch the children survive

pkill -x tips-supervisor; sleep 0.5; pgrep -x -c tips-worker

The supervisor is gone and both workers are still running. Nothing about a signal descends a process tree, so a script that ends its parent has not ended its work.

Show output
2

See who adopted the orphans

pkill -x tips-supervisor; sleep 0.5; ps -o ppid= -C tips-worker | tr -d " " | sort -u

A process whose parent exits is reparented to PID 1, so both workers now report the same new parent. An orphaned worker is easy to miss for that reason: it sits under nothing you would have thought to look at.

Show output
1

Signal the children by their parent

pkill -P "$(pgrep -x tips-supervisor)"; sleep 0.5; pgrep -x -c tips-worker; pgrep -x -c tips-supervisor

-P selects by parent PID, so this reaches the two workers and not the supervisor. The supervisor goes anyway, because it was waiting on those children and has nothing left to wait for.

Show output
0
0

Signal a whole process group

kill -TERM -"$(ps -o pgid= -p "$(pgrep -x tips-supervisor)" | tr -d " ")"; sleep 0.5; pgrep -c -x tips-worker; pgrep -c -x tips-supervisor

A negative number is a process group id rather than a PID. The supervisor was started with setsid, so it and its two workers share one group, and one signal reaches all three.

Show output
0
0

Read the group id off a process

ps -o pgid= -p "$(pgrep -x tips-supervisor)" | tr -d " " | wc -l

ps -o pgid= prints the group without a header, right-aligned in a column, so the tr strips the padding. One line out, and the negative argument above takes that number.

Show output
1

Signal every process in your own group

kill -TERM -$$

-$$ is the shell's own group, so this ends the shell and everything running under it. There is no output block because the command would take the replay's own shell with it. Think twice before typing it anywhere else for the same reason.

Signal names, numbers and the two kills

List the signals

kill -l | head -3

The bash builtin prints them numbered, in columns. The first fifteen are the ones worth recognising; everything from 34 up is a real-time signal that no ordinary program uses.

Show output
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM

Convert a number to a name

kill -l 9

Handy for reading a log line or an exit status. A shell reports a process killed by signal N as exit status 128+N, so status 137 is 128+9 and this is how you confirm it.

Show output
KILL

See the procps table instead

/bin/kill -L | head -4

-L is the binary's own listing, laid out in aligned columns with the SIG prefix dropped. The builtin has no such option.

Show output
 1 HUP      2 INT      3 QUIT     4 ILL      5 TRAP     6 ABRT     7 BUS
 8 FPE      9 KILL    10 USR1    11 SEGV    12 USR2    13 PIPE    14 ALRM
15 TERM    16 STKFLT  17 CHLD    18 CONT    19 STOP    20 TSTP    21 TTIN
22 TTOU    23 URG     24 XCPU    25 XFSZ    26 VTALRM  27 PROF    28 WINCH

Find out which kill you are running

type -a kill | head -2

The builtin comes first, so an interactive shell and a bash script both use it. sh -c, sudo kill and find -exec kill all reach the binary instead.

Show output
kill is a shell builtin
kill is /usr/bin/kill

See the binary's error message

/bin/kill 999999

The two disagree about wording. The builtin prefixes its message with the shell's name, and with the line number when it is in a script, so a log grep has to allow for both forms.

Show output
/bin/kill: (999999): No such process

Try to signal a process you do not own

sudo -u user kill "$(pgrep -x tips-stubborn)"

Signalling is permitted between processes of the same owner, or from root to anything. tips-stubborn belongs to root, so user is refused. sudo runs kill without a shell, so the message is the binary's.

Show output

Your output will differ: the PID will differ

kill: (2412): Operation not permitted

See which packages the three commands come from

dpkg -S $(command -v pkill) $(command -v killall)

pkill and pgrep are part of procps; killall is part of psmisc. Two packages, and a machine can have one without the other.

Show output
procps: /usr/bin/pkill
psmisc: /usr/bin/killall

Check which of the two Debian considers essential

dpkg-query -W -f='${Package} ${Priority}\n' procps psmisc

important means every Debian install has it. optional means a standard install has it and a minimal one may not, so killall is the one to avoid depending on in a script that has to run inside a container.

Show output
procps important
psmisc optional