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
How to Write on Serial Port Using Qextserialport
Statically Linked Shared Object? or a Corrupt File
Split and Rename The Splitted Files in Shell Script
Awk Command to Create Sha2 of Individual Column and Paste into New File
Sending Mail in Bash Script Outputs Literal \N Instead of a New Line
How to Take Screenshot of Obscured Window in C++ on Linux
Find Is Returning "Find: .: Permission Denied", But I Am Not Searching In
Is There Any Difference Between '=' and '==' Operators in Bash or Sh
Linux - Run Android Emulator on Nouveau Driver
Tensorflow Recommended System Specifications
Add a Directory When Creating Tar Archive
Remove Strings by a Specific Delimiter
Cython Standalone Executable on Ubuntu
Deleting All Files Except Ones Mentioned in Config File
Shell Programming: Executing Two Applications at The Same Time
Cron Job Mysteriously Stopped Running