opscape

Processes and jobs · bash builtin

kill

Send a signal to processes by PID or job number; by default TERM, which asks a process to exit.

kill [-s SIGNAL | -SIGNAL] PID|%JOB...   or   kill -l [SIGNAL|STATUS]

11 of 11 examples run in Debian 13.7 on 2026-10-31 · manual: kill(1)

Despite the name, kill sends signals, and only some of them end a process. The default is TERM (15), a polite request: the program can catch it, flush its files, remove its PID file and exit. KILL (9) cannot be caught or ignored; the kernel removes the process at once, with no cleanup. Other everyday signals: HUP (1) makes many daemons reload their configuration, INT (2) is what Ctrl-C sends, STOP and CONT pause and resume, and USR1/USR2 mean whatever the program defines (log rotation, a status dump).

The usual sequence for a stuck process is kill PID, wait a few seconds, check with kill -0 PID, and only then kill -9 PID. Going straight to -9 leaves temp files, lock files and half-written data behind.

In bash, kill is a builtin, which is why it understands job specs like %1 and works even when the process table is full and no new process can start. There is also a standalone /bin/kill (from procps on Linux) with a few extra options. To kill by name rather than PID, use pkill or killall. When a process dies from a signal, the shell reports its exit status as 128 plus the signal number: 143 for TERM, 137 for KILL.

Examples

Ask a process to terminate

sleep 300 &
kill $!
wait $!
echo "exit status: $?"
output
exit status: 143

$! is the PID of the most recent background job. kill sends TERM; wait collects the job's status, 143 = 128 + 15, meaning "killed by signal 15".

List the signal names and numbers

kill -l
explain
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
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

Every signal Linux defines. Numbers 1 to 15 are the same on every Unix-like system; the higher ones differ (USR1 is 30 on macOS), so use names in scripts. kill -l also works with a name or number (kill -l 9 prints KILL).

Find out which signal killed a process

kill -l 143
kill -l 137
kill -l HUP
output
TERM
KILL
1

Given an exit status above 128, bash's kill -l subtracts 128 and prints the signal name, so a $? of 137 decodes to KILL (often the kernel's out-of-memory killer). Given a name, it prints the number.

Kill a process that will not exit

bash -c 'trap "echo ignoring TERM" TERM; while :; do sleep 0.1; done' &
sleep 0.3
kill $!
sleep 0.3
kill -0 $! && echo "still running"
kill -9 $!
wait $!
echo "exit status: $?"
output
ignoring TERM
still running
/bin/bash: line 7:  1273 Killed                  bash -c 'trap "echo ignoring TERM" TERM; while :; do sleep 0.1; done'
exit status: 137

The child traps TERM and keeps going. kill -0 sends no signal, only checks that the process exists and you may signal it. kill -9 (-KILL) cannot be trapped; bash prints a Killed notice for the job and the status is 137 = 128 + 9.

Check whether a process is still alive

sleep 300 &
pid=$!
if kill -0 "$pid" 2>/dev/null; then echo "$pid is running"; fi
kill "$pid"; wait "$pid"
kill -0 "$pid" 2>/dev/null || echo "$pid is gone"
output
1283 is running
1283 is gone

Signal 0 performs the existence and permission checks without delivering anything, so the exit status answers "is this PID alive and mine?". Note it also fails for a live process owned by another user.

Tell a daemon to reload its configuration

daemon.sh
#!/bin/bash
trap 'echo "reloading config"' HUP
trap 'echo "shutting down cleanly"; exit 0' TERM
echo "started as $$"
while :; do sleep 0.1; done
bash daemon.sh &
sleep 0.3
kill -HUP $!
sleep 0.3
kill $!
wait $!; echo "exit status: $?"
output
started as 1285
reloading config
shutting down cleanly
exit status: 0

The script handles HUP by reloading and TERM by cleaning up and exiting 0, exactly what nginx, sshd and most daemons do. -HUP, -s HUP and -1 are equivalent spellings.

Pause and resume a process

sleep 300 &
sleep 0.1
kill -STOP $!
ps -o pid,stat,args -p $!
kill -CONT $!
ps -o pid,stat,args -p $!
output
    PID STAT COMMAND
   1295 T    sleep 300
    PID STAT COMMAND
   1295 S    sleep 300

STOP freezes the process (state T) without ending it, CONT resumes it (back to S, sleeping). Useful to pause a CPU-heavy job temporarily; like KILL, STOP cannot be caught.

Kill background jobs by job number

sleep 300 &
sleep 600 &
jobs
kill %1 %2
wait
jobs
output
[1]-  Running                 sleep 300 &
[2]+  Running                 sleep 600 &
[2]   Terminated              sleep 600

%1 and %2 refer to entries in the shell's job table, so you do not need the PIDs. Only the builtin kill understands % job specs; /bin/kill %1 does not. After wait, the job table is empty.

Kill a whole process group

set -m
sleep 300 | sleep 301 &
pgid=$(jobs -p %1)
pgrep -a -g "$pgid"
kill -- -"$pgid"
wait
pgrep -c -g "$pgid"
outputexit 1
1302 sleep 300
1303 sleep 301
0

With job control on (set -m), each job gets its own process group, whose ID is the PID of its first process; jobs -p prints it. A negative PID signals every process in the group, and -- stops -123 being read as a signal option. Afterwards pgrep -c finds nothing, prints 0 and exits 1. This is how to stop a pipeline or a script together with everything it started.

See which kill you are running

type -a kill
/bin/kill -L | head -3
output
kill is a shell builtin
kill is /usr/bin/kill
kill is /bin/kill
 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

type -a shows the bash builtin first, then the procps programs on PATH. The external /bin/kill has extras such as -L (a signal table) and --timeout, but the builtin is what runs when you type kill.

Signal a PID that does not exist

kill 99999
explain
outputexit 1
/bin/bash: line 1: kill: (99999) - No such process

No such process and exit status 1. A script that kills a PID read from a stale PID file should expect this, and treat it as "already stopped".

Common pitfalls

  • Reaching for kill -9 first. The process gets no chance to clean up: temp files stay, locks stay held, buffered writes are lost and child processes may be orphaned. Send TERM, wait, and escalate only if needed.

  • Trusting a PID file blindly. PIDs are reused, so after a crash the number may belong to an unrelated process. Check what it is first (ps -p "$pid" -o args=) or use pkill -F pidfile, which also checks.

  • Writing kill -15 -1 or kill -9 -1: -1 as a PID means every process you are allowed to signal, which ends your whole session (as root, nearly everything).

  • Expecting kill to wait. It returns as soon as the signal is sent, before the process has exited. In a script, follow it with wait "$pid" (for your own children) or poll with kill -0.

  • Using signal numbers above 15 in portable scripts. USR1, CHLD and STOP have different numbers on Linux and macOS; use names.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • The signals 1 to 15 have the same numbers everywhere; above that they differ (USR1 is 10 on Linux and 30 on macOS, STOP 19 vs 17). Names are portable.

  • procps /bin/kill adds options the bash builtin lacks, such as -L (signal table) and --timeout (send a follow-up signal after a delay). macOS /bin/kill has neither.

Options worth knowing

A selection, described in our own words. For the full list, see kill(1).

OptionWhat it does
-s SIGNALSend SIGNAL, given by name (TERM, HUP) or number.
-n SIGNUMSend the signal with number SIGNUM (bash builtin).
-lList signal names, or translate a name, number or exit status.
-LList signal names and numbers as a table (bash builtin and procps kill).
-9Send KILL, which cannot be caught or ignored.
-15Send TERM, the default polite termination request.
-1Send HUP, often used to make a daemon reload its configuration.
-0Send no signal; only check that the process exists and may be signaled.
-KILLSend KILL by name (same as -9).
-TERMSend TERM by name (same as -15).
-HUPSend HUP by name (same as -1).
-INTSend INT, the signal Ctrl-C sends (same as -2).
-QUITSend QUIT, the signal Ctrl-\ sends; many programs dump core on it.
-USR1Send USR1, whose meaning each program defines (often reopen logs).
-STOPPause the process; it cannot be caught.
-CONTResume a stopped process.

Related