Bash Signals Traps

A signal is a short message that the system sends to a running program. Pressing Ctrl+C sends one. A trap lets a script catch a signal and run cleanup code before it stops. Traps protect temporary files and half-finished work.

A Doorbell Analogy

A doorbell rings and the person inside decides what to do: open the door, ignore the sound, or finish a task first. A signal acts like the bell, and a trap holds the plan for the person inside.

  Keyboard Ctrl+C          kill command          System shutdown
        |                       |                      |
        +-----------+-----------+----------------------+
                    |
                    v
              +-----------+
              |  SIGNAL   |
              +-----+-----+
                    |
          +---------+----------+
          |                    |
          v                    v
     No trap set          trap defined
     Script stops         Cleanup code runs

Common Signals

SignalNumberTrigger
SIGINT2Ctrl+C from the keyboard
SIGTERM15Polite request to stop (default for kill)
SIGHUP1Terminal window closes
SIGKILL9Forced stop, cannot be caught
SIGSTOP19Pause the program, cannot be caught
EXITnoneBash special event: the script ends for any reason

Run trap -l to see the complete list on your system.

Basic Trap Syntax

trap 'commands' SIGNAL

Quotation marks wrap the commands. A function name works inside the quotes as well.

Catch Ctrl+C

#!/bin/bash
trap 'echo " Interrupt caught. Stopping safely."; exit 1' SIGINT

echo "Working... press Ctrl+C to stop"
while true
do
  sleep 1
done

Output after pressing Ctrl+C:

Working... press Ctrl+C to stop
^C Interrupt caught. Stopping safely.

Clean Up Temporary Files With EXIT

The EXIT trap runs when the script finishes normally, fails, or gets interrupted. It makes the best place for cleanup.

#!/bin/bash
temp_file=$(mktemp)

cleanup() {
  rm -f "$temp_file"
  echo "Temporary file removed"
}
trap cleanup EXIT

echo "Writing data to $temp_file"
echo "sample data" > "$temp_file"
cat "$temp_file"

Output:

Writing data to /tmp/tmp.k3J9xQ2aBd
sample data
Temporary file removed

The cleanup function runs even if the script crashes halfway.

Handle Several Signals

#!/bin/bash
handle_stop() {
  echo "Received stop request. Saving progress..."
  exit 0
}
trap handle_stop SIGINT SIGTERM

echo "Process ID: $$"
while true
do
  sleep 2
done

The variable $$ holds the process ID of the script. Open a second terminal and run kill PID with that number to send SIGTERM and watch the handler run.

Ignore a Signal

An empty command string makes the script ignore the signal:

trap '' SIGINT
echo "Ctrl+C is disabled for 5 seconds"
sleep 5
trap - SIGINT
echo "Ctrl+C works again"

The dash form trap - SIGINT restores the default behavior.

Trap Errors With ERR

The ERR event fires when a command fails:

#!/bin/bash
trap 'echo "Problem on line $LINENO"' ERR

echo "Step one"
ls /missing_folder
echo "Step two"

Output:

Step one
ls: cannot access '/missing_folder': No such file or directory
Problem on line 5
Step two

The variable $LINENO reports the line number of the failed command. Add set -e at the top to stop the script after the first failure.

Send Signals With kill

kill -SIGTERM 4521     # polite stop
kill -SIGINT 4521      # same as Ctrl+C
kill -9 4521           # forced stop, no cleanup runs

Try SIGTERM first. The forced signal skips every trap, so temporary files stay behind.

Practical Example: Lock File Guard

A lock file prevents two copies of a script from running at once. A trap removes the lock when the script ends.

#!/bin/bash
lock="/tmp/myjob.lock"

if [ -e "$lock" ]; then
  echo "Another copy is running"
  exit 1
fi

touch "$lock"
trap 'rm -f "$lock"' EXIT

echo "Job started"
sleep 10
echo "Job finished"

Key Takeaways

  • A signal is a message sent to a running program.
  • trap 'commands' SIGNAL defines what happens when the signal arrives.
  • The EXIT trap suits cleanup work.
  • SIGKILL and SIGSTOP cannot be caught.
  • Use trap - SIGNAL to restore default behavior.