Bash trap Ctrl+C (SIGINT): catch, ignore, and cleanup with `trap`

Bash trap Ctrl+C (SIGINT): linux ctrl c signal basics, trap INT and trap ctrl-c patterns, ignore with trap empty string, EXIT cleanup, quoting pitfalls, and subshell vs child-process behavior.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Bash trap Ctrl+C (SIGINT): catch, ignore, and cleanup with `trap`

Pressing Ctrl+C in a terminal sends SIGINT to the foreground process group so a runaway command can stop. In a script you handle that with trap: register a command list for INT or SIGINT, and Bash runs that list when the signal arrives. Inside the handler you can exit, run cleanup, or temporarily ignore the interrupt with an empty trap.

Tested all the commands from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.

Long-running loops are where this usually matters; see bash while loop for structure, then wire trap around them.


Ctrl+C, SIGINT, and the linux ctrl c signal

On typical Linux terminals, Ctrl+C generates SIGINT (signal 2). You can see the name in Bash’s signal list:

bash
trap -l | head -1

Output:

text
1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP

SIGKILL and SIGSTOP cannot be caught or ignored—design choice of the kernel, not Bash.


Bash trap ctrl c (trap … INT)

Minimal registration (names INT and SIGINT are equivalent in trap):

bash
trap 'echo interrupted; exit 0' INT

To exercise the handler without typing Ctrl+C in this article, you can deliver the same signal explicitly:

bash
bash -c 'trap "echo caught" INT; kill -INT $$; echo after'

Output:

text
caught
after

Here the handler does not exit, so the shell keeps going and prints after. If your handler calls exit, lines after the signal will not run—often what you want for a bash catch ctrl c cleanup path.

Calling a function keeps noisy logic out of the trap string:

bash
bash -c 'f(){ echo in_func; }; trap f INT; kill -INT $$; echo after_func'

Output:

text
in_func
after_func

Cleanup on any exit (EXIT)

EXIT (signal number 0 in trap’s world) runs when the shell exits normally, on exit, or on many interrupts—handy for rm’ing temp files once.

bash
bash -c 'trap "echo on_exit" EXIT; echo body'

Output:

text
body
on_exit

Combine INT, TERM, and EXIT when you want one cleanup path for “user pressed Ctrl+C”, “kill on the script”, and “finished successfully”.


Ignore Ctrl+C in a script (trap '' INT)

An empty command string tells Bash to ignore SIGINT for that shell until you reset it:

bash
trap '' INT

Use sparingly: operators expect Ctrl+C to stop whatever is in the foreground. Prefer short critical sections or document why interrupts are disabled.


Reset to default (trap - INT)

trap - INT removes the INT handler and restores the default disposition (usually terminate the process).


Quoting: why trap '…' INT matters for bash catch ctrl c

trap records a string and evaluates it later. With double quotes, variables expand when trap runs, not when the signal fires; single quotes defer expansion until the trap executes.

bash
bash -c 'x=1; trap "echo x is $x" INT; x=2; kill -INT $$'

Output:

text
x is 1
bash
bash -c 'x=1; trap '\''echo x is $x'\'' INT; x=2; kill -INT $$'

Output:

text
x is 2

(The second example nests quotes so the inner $x is literal in the outer shell until the trap fires.)


Subshells, children, and Git Bash Ctrl+C

A trap set inside ( … ) applies only to that subshell’s lifetime. A background sleep is a different process: it does not “inherit” your trap body; stopping sleep with Ctrl+C in the terminal sends SIGINT to the foreground job’s process group, which may or may not include your script depending on how jobs are set up.

Git Bash on Windows still runs Bash; trap / SIGINT behavior for scripts matches the Bash manual closely enough that everything above applies—terminal and pseudo-TTY details differ, not the basic trap '…' INT model.


Summary

Ctrl+C normally sends SIGINT. Register a handler with trap '…' INT (or SIGINT), run cleanup or print a message, and call exit when you want the script to stop. trap … EXIT runs when the shell exits for many reasons, including normal completion. trap '' INT ignores interrupts until you reset with trap - INT. Use single quotes in trap when variables must expand at signal time, not when trap is parsed. SIGKILL and SIGSTOP cannot be trapped. Git Bash follows the same Bash rules for scripts; differences usually show up in the terminal layer, not in trap itself.

To factor cleanup helpers or option parsing into reusable units, Bash functions pair naturally with trap, but remember traps follow the usual subshell rules when you wrap code in ( … ).

Further reading:

Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on …