Bash: Ctrl+C During Input Breaks Current Terminal

BASH: Ctrl+C during input breaks current terminal

Resetting your terminal should help. read -s is changing some settings to hide the input, and in some cases these settings aren't restored after the interrupt signal is received. Add this line to the beginning of the script to make sure your script resets the terminal to a good state:

trap 'stty sane' INT

As pointed out by User112638726, you should rather save the state of your terminal at the beginning of the script, and restore that (rather than assuming the original state is identical to what sane produces).

original_tty_state=$(stty -g)
trap "stty $original_tty_state" INT

Bash input read stays open after Ctrl+C

Your trap is simply calling f_menu recursively. When it returns, you go back to where you were when the signal happened, which is in the read command.

I tried putting

trap 'continue' SIGINT

in the while loop, but that didn't work well. After typing Control-c I had to press Return for it to work.

The solution I found was to put

trap 'return' SIGINT

in each of the functions that displays a menu.

#!/bin/bash

var_answer1 = 0
var_answer2 = 0

f_blah1(){
printf "Whatever 1"
}

f_blah2(){
printf "Whatever 2"
}

f_option1(){
trap 'return' SIGINT
printf "====================================================================\n"
printf "What do you want to do?\n"
printf "====================================================================\n"
printf "1) Do this\n"
printf "2) Do that\n"
printf "====================================================================\n"
printf "Enter your choice: "
read var_answer2
printf "====================================================================\n\n"

if [ "$var_answer2" -eq "1" ]; then
f_blah1
elif [ "$var_answer2" -eq "2" ]; then
f_blah2
else
printf "====================================================================\n"
printf "'$var_boxChoice' is not a viable answer!\nYou can only enter numbers 1 and 2!\n";
printf "====================================================================\n"
fi
}

f_option2(){
printf "Meh. I'm sleeping for 5 seconds. Then I'll return to main.\n"
}

f_menu(){
trap 'return' SIGINT
printf "====================================================================\n"
printf "Select an option:\n"
printf "====================================================================\n"
printf "1) Option 1\n"
printf "2) Option 2\n"
printf "3) Quit\n"
printf "====================================================================\n"
printf "Enter your choice: "
read var_answer1
printf "====================================================================\n\n"

if [ "$var_answer1" -eq "1" ]; then
f_option1
elif [ "$var_answer1" -eq "2" ]; then
f_option2
elif [ "$var_answer1" -eq "3" ]; then
printf "====================================================================\n"
printf "Quitting the app... Thanks for using it!\n"
printf "====================================================================\n"
exit
else
printf "====================================================================\n"
printf "'$var_answer' is not a viable answer!\nYou can only enter numbers 1-3!\n";
printf "====================================================================\n"
fi
}

while :
do
f_menu
done

Bash - Break out of loop with Ctrl-C but continue with script

Trap Statement

Here is one method for making sure the echo statements are run after a Ctrl+C:

trap printout SIGINT
printout() {
echo ""
echo "Finished with count=$count"
exit
}
while :
do
((count++))
sleep 1
done

When run and Ctrl+C is pressed, the output from this script looks like:

$ bash s.sh
^C
Finished with count=2

How it works

The trap statement captures Ctrl+C and executes the function printout. That function can include any statement you like.

Subshell with trap

Alternatively, we can put the loop and the trap statement in a subshell:

$ cat t.sh
(
trap printout SIGINT
printout() {
echo ""
echo "At end of loop: count=$count"
exit
}
while :
do
((count++))
sleep 1
done
)
echo "Finishing script"

when this is run and Ctrl+C is pressed, the output looks like:

$ bash t.sh
^C
At end of loop: count=2
Finishing script

This method allows us to continue with the script after the subshell. Note, though, that any variables set or other environment changes made in the subshell are lost after the subshell exits.

ctrl-c being inserted into command line

From this SuperUser question, this is what you need to do to get back to the old behaviour of ctrl+c:

stty -echoctl

I've just tried it on my own CentOs 6 machine and verified that it does what (I think) you're looking for :)

How to prevent a user from using ctrl-c to stop a script?

You can always trap SIGINT:

trap 'echo got SIGINT' SIGINT

Once you're done, reinstall the default handler again with

trap SIGINT

See the POSIX spec for trap for details. This works in all Bourne shells, not just bash.

How to send control+c from a bash script?

Ctrl+C sends a SIGINT signal.

kill -INT <pid> sends a SIGINT signal too:

# Terminates the program (like Ctrl+C)
kill -INT 888
# Force kill
kill -9 888

Assuming 888 is your process ID.


Note that kill 888 sends a SIGTERM signal, which is slightly different, but will also ask for the program to stop. So if you know what you are doing (no handler bound to SIGINT in the program), a simple kill is enough.

To get the PID of the last command launched in your script, use $! :

# Launch script in background
./my_script.sh &
# Get its PID
PID=$!
# Wait for 2 seconds
sleep 2
# Kill it
kill $PID

Idle bash script until CTRL+c event is logged

Assuming you're connected to a TTY:

# idle waiting for abort from user
read -r -d '' _ </dev/tty

Echo Control C character

No, piping a CTRL-C character into your process won't work, because a CTRL-C keystroke is captured by the terminal and translated into a kill signal sent to the process.

The correct character code (in ASCII) for CTRL-C is code number 3 but, if you echo that to your program, it will simply receive the character from its standard input. It won't cause the process to terminate.

If you wanted to ease the task of finding and killing the process, you could use something like:

./program_that_does_not_terminate &
pid=$!
sleep 20
kill ${pid}

$! will give you the process ID for the last started background process.



Related Topics



Leave a reply



Submit