I need help understanding exactly how SIGINT is propagated between a shell script's main flow and a function call inside that script. For my script, I have a main menu which takes user input and calls a child function based on that input. The child function interacts with the user.
My goals are the following:
The behavior I see is that I can hit ctrl+c either inside the main menu or from inside the child function and it will work as expected the first time, but all subsequent ctrl+c signals are ignored.
I feel like my issue is very close to these:
Though in their case they are calling the child in a new process, and I'm calling a sourced function, which I don't think would have the same implications for fg/bg, would it?
For a minimal example, let's say I have a file called main.sh:
trap catchInt SIGINT
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
menu
}
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
source child.sh
# I also source other scripts with utility functions and exported vars
menu
And if I have a file called child.sh in the same directory:
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
Here is an example run of the above code in which I hit ctrl+c inside the menu, and then try again inside the child function:
bash main.sh
Please choose a number and press enter.
caught sigint
reallyquit is now 1
Please choose a number and press enter.
1
Please type something^C^C^C^C^C (I finally pressed enter)
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
Here is an example in which I first type 1, then ctrl-c:
bash main.sh
Please choose a number and press enter.
1
Please type something^Ccaught sigint
reallyquit is now 1
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
How can I get the trap to respond every time I send the INT signal?
I do not know for sure, but I think it's because you're still in the "trap handler" when menu
is called for the second time. Because a sigint is still being handled, a second sigint isn't processed. If you would remove that call and wrap menu
with while true; do ...; done
, it does work:
#! /bin/bash
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
}
trap catchInt SIGINT
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
# I also source other scripts with utility functions and exported vars
while true; do
menu
done
exit 0
EDIT:
Why do you include the child.sh in main.sh? Usually one would create common functions and include it in the child script. This way you can share the functions in main.sh among child1.sh,... , childN.sh. If would add source main.sh
into child.sh, the traps will work as well.