kill
Send a signal to a process by id or by name
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
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
Convert a name to a number
kill -l TERM
The lookup in the other direction. SIGTERM works as well as TERM.
Show output
15
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