Use the following code :

#!/bin/bash
# type "finish" to exit

stty -echoctl # hide ^C

# function called by trap
other_commands() {
    tput setaf 1
    printf "\rSIGINT caught      "
    tput sgr0
    sleep 1
    printf "\rType a command >>> "
}

trap 'other_commands' SIGINT

input="$@"

while true; do
    printf "\rType a command >>> "
    read input
    [[ $input == finish ]] && break
    bash -c "$input"
done
Answer from Gilles Quénot on Stack Overflow
🌐
Rimuhosting
rimuhosting.com › knowledgebase › linux › misc › trapping-ctrl-c-in-bash
Trapping ctrl-c in Bash - RimuHosting
e.g. if you need to perform some cleanup functions. #!/bin/bash # trap ctrl-c and call ctrl_c() trap ctrl_c INT function ctrl_c() { echo "** Trapped CTRL-C" } for i in `seq 1 5`; do sleep 1 echo -n "." done
Discussions

How to handle ctrl+c in bash scripts
cleanup(){ echo "" echo "CTRL+C pressed, clean up things before exiting..." rm -rf touch.tmp 2>/dev/null exit 1 } # Trap the SIGINT signal (Ctrl+C) trap cleanup SIGINT You should not run exit in a SIGINT trap. If you do, shell scripts that run your script will no longer abort when you hit Ctrl+C. A correct way to do this is: sigint_handler() { printf '\nCTRL+C pressed, clean up things before exiting...\n' rm -f "$tmpdir/touch.tmp" # reset SIGINT handler to SIG_DFL, then resend signal to self trap - INT kill -INT "$$" } trap sigint_handler INT See https://www.cons.org/cracauer/sigint.html for a thorough explanation of why. Another way is to use an EXIT trap instead, which in bash is triggered both when a "normal" exit happens as well as when it gets killed by a trapable signal, such as SIGINT cleanup() { printf '\nCleaning up before exiting...\n' rm -f "$tmpdir/touch.tmp" } trap cleanup EXIT On a side note, it's not really a good idea to pass a relative path to rm in such a trap. If the script changes directory at any point, it may end up not removing the intended file because it's in another directory, or worse, it could end up removing the wrong file. More on reddit.com
🌐 r/bash
11
0
July 21, 2024
Trap Ctrl C in script - Unix & Linux Stack Exchange
I have experimented with the answer from jesse_b above, but it didn't work for me because the process before the Ctrl+C is kinda a blocking process, so what I would do is to put the function before the trap call. #!/bin/bash trp() { sudo airmon-ng stop wlp2s0mon service NetworkManager start ... More on unix.stackexchange.com
🌐 unix.stackexchange.com
signals - Termination of ssh with Ctrl-C trap in bash script - Unix & Linux Stack Exchange
When I press Ctrl+C with the first script, it works as I expect, Ctrl+C doesn't have any effect #!/bin/bash trap '' INT ssh user@server 'svn checkout ...' echo "done" But with the second script Ct... More on unix.stackexchange.com
🌐 unix.stackexchange.com
September 22, 2022
Trap 'Ctrl + c' for bash script but not for process open in this script - Unix & Linux Stack Exchange
I tried to have an interactive program in a bash script : my_program And I wish to be able to close it with 'Ctrl + c'. But when I do it my script is closing as well. I know about. trap '' 2 More on unix.stackexchange.com
🌐 unix.stackexchange.com
Top answer
1 of 1
2

Here is how you can trap the CTRL+C in the script. You don't call the ctrl_c function as that will be called when you press CTRL+C on the keyboard. I added in an exit 0 so that it exits the script after it traps the CTRL+C and stops looping through the for .. loop.

cat ctrl_test.bsh 
#!/bin/bash

#Setup trap command, assign what to call and what is the trigger (SIGINT)
trap ctrl_c INT

#Function of what trap command calls    
function ctrl_c() {
    printf "\n** Trapped CTRL-C after $i seconds.\n"
    exit 0
}

#Rest of script to print counting numbers to screen will not break with CTRL+C due to trap
for ((i=1;i>0;i++))
do
    printf "\r$i"
    sleep 1
done

Example:

terrance@terrance-ubuntu:~$ ./ctrl_test.bsh 
13^C
** Trapped CTRL-C after 13 seconds.
terrance@terrance-ubuntu:~$ 

In your script case you could do something like this:

#TimerInTerminal.sh

#Trap Ctrl+C and re-enable suspend and hibernation modes
trap ctrl_c INT
function ctrl_c() {
    sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target
    exit 0
}

# To prevent your Linux system from suspending or going into hibernation, you need to disable the following systemd targets:
# sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

# To re-enable the suspend and hibernation modes, run the command:
# sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target

soundfile="/usr/share/sounds/My_Sounds/Electronic_Chime.wav"
# Stop computer from sleeping while timer is running

# prevent your Linux system from suspending or going into hibernation
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

# This allows supend ?
#trap "echo marlin | sudo -S systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target" INT EXIT

if [ $# -eq 1 ]
then
    DURATION="$1"
else
    read -r -p "Timer for how many minutes?( for fractional, use decimal notation , 0.5==30s, 1.25==75s etc) : "  DURATION
    read -r -p "Enter text to display at the end of the timer : " n1
fi

DURATION=$(echo "$DURATION * 60 / 1" | bc) # lets us deal with fractional inputs

START=$(date +%s)   # only do this once (anchor's the time)

countdown () {
    NOW=$(date +%s)              # Get time now in seconds
    DIF=$((NOW - START))         # Compute diff in seconds
    ELAPSE=$((DURATION - DIF))   # Compute elapsed time in seconds
    MINS=$((ELAPSE / 60))        # Convert to minutes... (dumps remainder from division)
    SECS=$((ELAPSE - (MINS*60))) # ... and seconds
    #banner "$MINS:$SECS"
    echo "$MINS:$SECS"
    sleep "$1"
}

while true 
do
    clear
    
    countdown 0 # calc time remaining
    
    if [ $MINS -le 0 ]
    then
        # Blink screen

        while [ $SECS -gt 0 ]
        do
            
            clear # Flash on
            #setterm -term linux -back red -fore white 
            countdown 0.5

            clear # Flash off
            #setterm -term linux -default
            countdown 0.5

        done # End for loop
        
        setterm -term linux -default
        clear
        
        break   # time has expired lets get out of here
    
    else
        countdown 1 
    fi
done

echo $n1
amixer -D pulse sset Master 30% > /dev/null 2>&1
# Play a sound
cvlc --play-and-exit "$soundfile" > /dev/null 2>&1

# To re-enable the suspend and hibernation modes, run the command:
 echo marlin | sudo -S systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target
🌐
Reddit
reddit.com › r/bash › how to handle ctrl+c in bash scripts
r/bash on Reddit: How to handle ctrl+c in bash scripts
July 21, 2024 -

Hello Guys!

I have wrote an article on Medium on how to handle ctrl+c in bash scripts using the 'trap' command

For Medium users with a subscription: https://lovethepenguin.com/how-to-handle-ctrl-c-in-bash-scripts-d7085e7d3d47

For Medium users without a subscription: https://lovethepenguin.com/how-to-handle-ctrl-c-in-bash-scripts-d7085e7d3d47?sk=8a9020256b1498196a923c5521619228

Please comment on what you liked, did you find this article useful?

🌐
GoLinuxCloud
golinuxcloud.com › home › programming › bash trap ctrl+c (sigint): catch, ignore, and cleanup with `trap`
Bash trap Ctrl+C (SIGINT): catch, ignore, and cleanup with `trap` | GoLinuxCloud
1 month ago - 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.
🌐
Linux Journal
linuxjournal.com › content › bash-trap-command
The Bash Trap Command | Linux Journal
August 7, 2019 - The "SIGINT" signal is perhaps the only one that might be of interest in a script. SIGINT is generated when you type Ctrl-C at the keyboard to interrupt a running script. If you don't want your script to be stopped like this, you can trap the signal and remind yourself that interrupting the ...
Find elsewhere
Top answer
1 of 1
1

This other answer explains why you see trap worked only after done. If you run the subshell in the background and then wait for it, the result will be different:

#!/bin/bash

trap 'echo "trap worked"' INT

(
   trap '' INT
   sleep 5
   echo "done"
) &

wait

If you hit Ctrl+c when the subshell works, it will trigger the trap immediately. It will also make the script go past wait and thus finish. Run wait in a loop if you don't want the script to finish:

until wait; do :; done

This way you will be able to trigger the trap multiple times easily and the script will exit after the subshell exits.

I guess this is not exactly what you mean by "without interrupting the foreground task", since the task (here: sleep) is in the background instead and it has its consequences. When job control is disabled (and it is by default in a script), & redirects stdin to /dev/null or equivalent file. This way what you run in the background cannot steal input. If you want our background subshell to be able to read from the stdin of the whole script then you need to explicitly redirect stdin to stdin (<&0). It looks like a no-op, but with & it actually makes sense, it makes an asynchronous job behave more like a job in the foreground:

(…) <&0 &

Another thing: note wait without arguments waits for all currently active child processes. If your actual script runs more jobs in the background and you want wait not to wait for some of them, the easiest way may be to disown.


Example code:

#!/bin/bash

trap 'echo "trap worked"' INT

(
   trap '' INT
   sleep 10
   echo "(not waited for) done"
) &
disown "$!"

(
   trap '' INT
   cat
   echo "(waited for) done"
) <&0 &

until wait; do :; done

Notes:

  • cat and sleep are both immune to Ctrl+c.
  • You can strike Ctrl+c and trigger the trap multiple times.
  • At the same time you can write lines to cat and it will work, i.e. it will print upon Enter (but if you Ctrl+c in the middle of a line then what you typed so far (i.e. what hasn't been delivered to cat yet) will be discarded and never delivered to cat because this is how the line discipline works).
  • Exit cat with Ctrl+d before sleep 10 finishes and you will see the script does not wait for the disowned job.

Side note: while playing with SIGINT and bash, it's good to know bash implements a "wait and cooperative exit" approach at handling SIGINT/SIGQUIT delivery. See this link and this article.

🌐
SysTutorials
systutorials.com › trap-ctrl-c-in-a-bash-script
Handling Ctrl-C Interrupts In Bash Scripts - SysTutorials
April 12, 2026 - (Press Ctrl-C to stop)" sleep 1 done · You can trap multiple signals with a single handler or separate ones: #!/bin/bash cleanup() { echo "Caught signal, cleaning up..." exit 0 } # Trap both INT (Ctrl-C) and TERM (termination) trap cleanup INT TERM # Or use separate handlers: trap 'echo "Interrupted"; exit 1' INT trap 'echo "Terminated"; cleanup_on_term' TERM
🌐
InterServer
interserver.net › home › linux › how to use the trap command in bash
How to Use the trap Command in Bash - Interserver Tips
April 23, 2026 - #!/bin/bash TEMPFILE=$(mktemp) trap 'rm -f "$TEMPFILE"; echo "Cleaned up."' EXIT echo "Working with $TEMPFILE..." sleep 10 echo "Done!" Try running that, then hit Ctrl+C during the sleep. You’ll see “Cleaned up.” And if you check /tmp, your temp file will be gone.
🌐
Medium
lovethepenguin.com › how-to-handle-ctrl-c-in-bash-scripts-d7085e7d3d47
How to handle ctrl+c in bash scripts | by Konstantinos Patronas | Medium
July 12, 2024 - in this basic example we have a ... 1 echo "Running..." rm -f test.tmp done · Bash has the trap command, the trap command allows catching signals (ctrl+c ......
🌐
Unix.com
unix.com › shell programming and scripting
ctrl c trapping signal - Shell Programming and Scripting - Unix Linux Community
October 19, 2017 - im trying to make a trap signal 2 (ctrl c) in a bash script if a user presses ctrl c while running the script it should display an error message but not quit the bash script just yet.
🌐
Usrsb
usrsb.in › blog › blog › 2011 › 04 › 02 › using-trap-to-catch-ctrl-plus-cs-and-control-how-your-script-exits
Using 'trap' To Catch 'Ctrl + C's and Control How Your Script Exits - /usr/sbin/blog
April 2, 2011 - } trap 'exit_routine' INT # Intercept SIGINT and call exit_routine · If it’s short enough, you can, of course, cram your entire exit routine between the single quotes, but if it’s nontrivial it’s best to pull it out into its own function. Also remember that BASH executes a script’s commands in the order it sees them, so this must be placed somewhere near the beginning of the script to set the trap early on.
🌐
Stack Pointer
stackpointer.io › script › how-to-catch-ctrl-c-in-shell-script › 248
How to Catch Ctrl-C in Shell Script - Stack Pointer
May 13, 2014 - #!/bin/sh # this function is called when Ctrl-C is sent function trap_ctrlc () { # perform cleanup here echo "Ctrl-C caught...performing clean up" echo "Doing cleanup" # exit shell script with error code 2 # if omitted, shell script will continue ...
🌐
Unix.com
unix.com › shell programming and scripting
trap CTRL-C problem - Shell Programming and Scripting - Unix Linux Community
October 19, 2017 - I am trying to trap CTRL-C, now the program I call has it's own exit message, I think this is the problem .. This is what I have now : function dothis { echo 'you hit control-c' exit } function settrap { trap dothis SIGINT } settrap until false; do ./ITGRecv.exe done Doing this I am able to trap it but only after pressing CTRL-C twice, the first time I press it it just runs my program again.
🌐
GitHub
gist.github.com › RooSoft › 5c2a79e9fd9c6d4a50ccc4cef1d09bdc
How to trap ctrl-c in a bash script · GitHub
How to trap ctrl-c in a bash script. GitHub Gist: instantly share code, notes, and snippets.
🌐
Northern Illinois University
faculty.cs.niu.edu › ~berezin › 330 › N › bash-trap.html
Bash trap
For the exercise below, copy the code into a file and make it executable. You may name it what ever you want. For discussion, I will assume it is called "adder". Run it and test it out. The 1st trap traps [ctrl]c signal and exits with a message and an error status.