Imagine that you've started a program in your terminal, and it refuses to finish. Perhaps a script has entered an infinite loop, a command is taking much longer than expected, or a background process is consuming resources without doing anything useful.
What do you do?
On Linux and other Unixlike systems, one of the standard ways to communicate with a running process is to send a signal. Bash provides a builtin command called kill for this purpose.
Despite its rather dramatic name, kill doesn't necessarily terminate anything. It can ask a process to shut down gracefully, stop or resume its execution, or notify it that something has happened.
Let's explore how the Bash kill builtin works, which signals you'll use most often, and how to control processes safely from your shell scripts.
1. What Is kill?
The kill builtin sends a signal to one or more processes or, in Bash, to jobs managed by the current shell.
Its basic syntax is:
kill [-s signal | -n signum | -signal] PID_or_jobThe most important parts are:
- Signal: The type of notification you want to send.
- PID: The process ID of a running process.
- Job specification: A reference to a job managed by your current shell, such as
%1.
If you don't specify a signal, kill sends SIGTERM by default.
For example:
kill 2244This sends SIGTERM to process ID 2244, requesting that the process terminate.
You can also specify the signal explicitly:
kill -s TERM 2244Or use its conventional name with a leading dash:
kill -TERM 2244These commands have the same intended effect.
One important distinction: kill sends a signal; it doesn't guarantee that the process has terminated. A process may handle the signal, ignore it where permitted, or take some time to shut down.
2. The Difference Between Processes and Jobs
Before going further, let's clarify two concepts that are easy to confuse.
A process is a running program instance managed by the operating system. Each process has a process ID, or PID.
A job is a command or pipeline managed by your current shell's job-control system. A job may contain one or more processes.
For example, start a long-running command in the background:
sleep 600 &Bash starts the command as a background job and displays information similar to this:
[1] 2244Here, 1 is the job number, while 2244 is the process ID shown for the background job.
You can inspect jobs managed by your shell with:
jobsExample output:
[1]+ Running sleep 600 &Now you can send a signal to the job:
kill %1Or target the process directly:
kill 2244The %1 notation refers to job number 1 in the current shell. It is not a PID, and it generally cannot be used to refer to a job owned by another shell.
This distinction is useful when working interactively. In scripts, PIDs are often more convenient when you need to manage a specific process.
3. Specifying Signals
The kill builtin provides several ways to specify a signal.
Using -s
The -s option specifies a signal by name:
kill -s TERM 2244Using -n
The -n option specifies a signal by number:
kill -n 15 2244On Linux, signal number 15 is SIGTERM.
However, signal numbers are not identical across all Unix-like operating systems. When portability matters, signal names are usually easier to understand and safer to maintain.
Using the signal name directly
You can also use the signal name with a leading dash:
kill -TERM 2244This is a common and readable form.
Listing available signals
Use the -l option to display signal names:
kill -lThe exact output depends on the operating system and shell implementation. On Linux, you can also request information about a particular signal number:
kill -l 15This normally displays:
TERMThe -l option is especially useful when you encounter a signal number in a script or diagnostic message and want to identify its name.
4. The Signals You Should Know
Linux supports numerous signals, but most everyday shell scripting requires only a handful.
SIGTERM — Request termination
Signal number 15 on Linux.
kill -TERM 2244This is the default signal sent by kill. It asks the process to terminate and gives it an opportunity to clean up, save data, or release resources.
It is usually the best first choice when stopping a process.
SIGKILL — Force termination
Signal number 9 on Linux.
kill -KILL 2244You can also use its numeric form:
kill -9 2244This signal cannot be caught, blocked, or ignored by the target process. The operating system terminates the process without giving it an opportunity to perform normal cleanup.
Use it only when a process cannot be stopped by a more graceful method.
SIGINT — Interrupt
Signal number 2 on Linux.
kill -INT 2244This signal is commonly generated when you press Ctrl+C in a terminal. It asks the foreground program to interrupt its work. Programs can handle it to perform cleanup or finish an operation safely.
SIGSTOP — Stop execution
Signal number 19 on Linux.
kill -STOP 2244This suspends a process without terminating it. Unlike SIGTERM, it cannot be caught or ignored. The process remains present but does not execute normally until resumed.
SIGCONT — Resume execution
Signal number 18 on Linux.
kill -CONT 2244This resumes a stopped process. It can also be delivered to a process that is not currently stopped, although the effect depends on the program and its state.
SIGHUP — Hangup
Signal number 1 on Linux.
kill -HUP 2244Traditionally, this signal is associated with a terminal or connection disappearing. Some daemons and other programs also use it as a request to reload their configuration.
Whether it reloads anything depends on the application. Don't assume that sending SIGHUP always reloads a program's configuration.
These are not the only available signals, but they cover many common process-management tasks. You can explore the complete list on your system with kill -l.
5. Gracefully Stopping a Process
Suppose you have started a background process and want it to stop.
Start a test process:
sleep 600 &
pid=$!
echo "Started process: $pid"The special variable $! contains the PID of the most recently started background process. Saving it allows you to target the correct process later.
Request termination:
kill "$pid"Because no signal was specified, Bash sends SIGTERM.
For a real application, this gives the program an opportunity to handle the request and shut down cleanly. For sleep, the process will ordinarily terminate promptly.
If the process doesn't stop, don't immediately assume that the signal failed. It may be handling the signal, taking time to finish work, or unable to make progress because of its current state.
You can check whether a process still exists using:
kill -0 "$pid"Signal number 0 is a special case: no signal is delivered. Instead, the command checks whether the process exists and whether the caller has permission to signal it.
A successful result does not prove that the process is healthy or doing useful work. It only indicates that the check succeeded.
Also remember that PIDs can be reused after a process exits, so a PID saved for a long time should not be treated as a permanent identity.
6. When a Process Refuses to Stop
Sometimes a process doesn't respond to SIGTERM. Perhaps it is stuck, has a bug, or deliberately ignores termination requests.
As a last resort, you can send SIGKILL:
kill -KILL "$pid"Or use its numeric form:
kill -9 "$pid"The process cannot handle SIGKILL itself, so it cannot use the signal to perform its own cleanup.
This is why SIGKILL should not be your first choice. The process may leave temporary files, incomplete operations, or other resources that normally would have been cleaned up.
A sensible approach is:
- Send
SIGTERMand allow the application time to exit. - Check whether the process has stopped.
- Investigate why it remains if necessary.
- Use
SIGKILLonly when graceful termination is unsuccessful and forceful termination is appropriate.
Even SIGKILL does not guarantee that a process disappears immediately. For example, a Linux process stuck in certain uninterruptible kernel waits may not complete termination until the underlying wait condition is resolved.
7. Stopping and Resuming Processes
Signals are not limited to terminating programs. They can also control execution.
Start a background process:
sleep 600 &
pid=$!Suspend it:
kill -STOP "$pid"Resume it:
kill -CONT "$pid"The process has not been terminated; its execution has been suspended and then resumed.
This is useful when you need to pause a process temporarily without losing its current state.
In interactive Bash, job-control commands such as Ctrl+Z, fg, and bg provide additional ways to manage jobs. These operate through the shell's job-control system and are often more convenient than sending signals manually.
8. Using kill in Bash Scripts
The kill builtin is especially useful in scripts that launch background processes and later need to stop them.
For example:
#!/usr/bin/env bash
sleep 600 &
pid=$!
cleanup() {
kill -TERM "$pid" 2>/dev/null || :
wait "$pid" 2>/dev/null || :
}
trap cleanup EXIT
echo "Background process started: $pid"
# Perform other work here.
sleep 2This example starts a background process and registers a cleanup function that runs when the shell exits.
The cleanup function sends SIGTERM and then uses wait to collect the child's exit status. Errors are suppressed because the child might already have exited by the time cleanup runs.
This is a useful starting pattern, but production scripts may need additional care. For example, if the child process exits and its PID is later reused, blindly signalling the saved PID could affect an unrelated process. More complex process management should track child lifetimes carefully and handle interruption and shutdown explicitly.
Another important distinction: kill reports whether the signal could be sent; it does not wait for the target to terminate. For a child process, wait is generally the appropriate way to wait for completion.
9. Is kill a Bash Builtin or an External Command?
Many Unix-like systems provide both a shell builtin named kill and an external executable with the same name.
In Bash, you can inspect the command resolution with:
type killTo explicitly invoke Bash's builtin, use:
builtin kill -TERM "$pid"This can be useful in scripts where you want to be certain you're using the shell builtin.
The builtin is particularly convenient because it understands Bash job specifications such as %1, which refer to jobs managed by the current shell.
For ordinary process signalling, the builtin is generally all you need.
Conclusion
The Bash kill builtin is a fundamental tool for process management. It allows you to send signals to processes and shell jobs, request graceful termination, force termination when necessary, and suspend or resume execution.
The most important rule is to choose the right signal for the job. Start with SIGTERM when you want a process to shut down, use SIGKILL only as a last resort, and remember that sending a signal is not the same as confirming that a process has terminated.
Once you understand signals, process IDs, and Bash job specifications, you'll have much better control over both interactive sessions and automated shell scripts.
And if a process refuses to cooperate, don't reach for SIGKILL immediately. Sometimes the best first step is simply to find out what the process is doing.