I am trying to handle SIGINT/CTRL+C interrupt in such a way that if a user accidentally presses ctrl-c, he is prompted with a message, Do you wish to quit?(y/n). If he enters yes, then exit the script. If no, then continue from where ever the interrupt occurred. Basically, I need Ctrl-C to work similar to Ctrl-Z/SINTSTP but in a slightly different way. I have tried various ways to acheieve this but I didn't get the expected results. Below are few scenario which I tried.Case:1Script : play.sh#!/bin/shfunction stop(){while true; do     read -rep $'\nDo you wish to stop playing?(y/n)' yn    case $yn in        [Yy]* ) echo Thanks for playing !!!; exit 1;;        [Nn]* ) break;;        * ) echo Please answer (y/n);;    esacdone} trap 'stop' SIGINT echo going to sleepfor i in {1..100}do  echo $i  sleep 3   doneecho end of sleepWhen I run the above script, I get the expected results.Output:$ play.sh going to sleep1^CDo you wish to stop playing?(y/n)yThanks for playing !!!$ play.sh going to sleep12^CDo you wish to stop playing?(y/n)n34^CDo you wish to stop playing?(y/n)yThanks for playing !!! $  Case:2 I moved the for loop to a new script loop.sh, thus play.sh becomes the parent process and loop.sh the child process.Script : play.sh#!/bin/shfunction stop(){while true; do     read -rep $'\nDo you wish to stop playing?(y/n)' yn    case $yn in        [Yy]* ) echo Thanks for playing !!!; exit 1;;        [Nn]* ) break;;        * ) echo Please answer (y/n);;    esacdone}trap 'stop' SIGINT loop.shScript : loop.sh#!/bin/shecho going to sleepfor i in {1..100}do  echo $i  sleep 3   doneecho end of sleepOutput in this case is not as expected.Output:$ play.sh going to sleep12^CDo you wish to stop playing?(y/n)yThanks for playing !!!$ play.sh going to sleep1234^CDo you wish to stop playing?(y/n)n$I understand that when a process receives a SIGINT signal, it propagates the signal to all the child processes, thus my 2nd case is failing. Is there any way that I can avoid SIGINT being propagated to child processes and thus make the loop.sh work exactly the way it worked in the 1st case?Note: This is just an example of my actual application. The application I am working on has several child scripts in play.sh and loop.sh. I should make sure that the application on receiving SIGINT, should not terminate but it should prompt the user with a message.
How can I handle SIGINT trap with a user prompt in shell script?
linux;shell;signals;interrupt;trap
Great classic question about managing jobs and signals with good examples!  I've developed a stripped down test script to focus on the mechanics of the signal handling.To accomplish this, after starting the children (loop.sh) in the background, call wait, and upon receipt of the INT signal, kill the process group whose PGID equals your PID.For the script in question, play.sh, this can be accomplished by the following:In the stop() function replace exit 1 withkill -TERM -$$  # note the dash, negative PID, kills the process groupStart loop.sh as a background process (multiple background processes can be started here and managed by play.sh)loop.sh &Add wait at the end of the script to wait for all children.waitWhen your script starts a process, that child becomes a member of a process group with PGID equal to the PID of the parent process which is $$ in the parent shell.For example, the script trap.sh started three sleep processes in the background and is now waiting on them, notice the process group ID column (PGID) is the same as the PID of the parent process:  PID  PGID STAT COMMAND17121 17121 T    sh trap.sh17122 17121 T    sleep 60017123 17121 T    sleep 60017124 17121 T    sleep 600In Unix and Linux you can send a signal to every process in that process group by calling kill with the negative value of the PGID.  If you give kill a negative number, it will be used as -PGID.  Since the script's PID ($$) is the same as it's PGID, you can kill your process group in the shell withkill -TERM -$$    # note the dash before $$you have to give a signal number or name, otherwise some implementations of kill will tell you Illegal option or invalid signal specification.The simple code below illustrates all of this.  It sets a trap signal handler, spawns 3 children, then goes into an endless wait loop, waiting to kill itself by the kill process group command in the signal handler.$ cat trap.sh#!/bin/shsignal_handler() {        echo        read -p 'Interrupt: ignore? (y/n) [Y] >' answer        case $answer in                [nN])                         kill -TERM -$$  # negative PID, kill process group                        ;;        esac}trap signal_handler INT for i in 1 2 3do    sleep 600 &donewait  # don't exit until process group is killed or all children dieHere's a sample run:$ ps -o pid,pgid,stat,args  PID  PGID STAT COMMAND 8073  8073 Ss   /bin/bash17111 17111 R+   ps -o pid,pgid,stat,args$ OK no extra processes running. Start the test script, interrupt it (^C), choose to ignore the interrupt, and then suspend it (^Z):$ sh trap.sh ^CInterrupt: ignore? (y/n) [Y] >y^Z[1]+  Stopped                 sh trap.sh$Check the running processes, note the process group numbers (PGID):$ ps -o pid,pgid,stat,args  PID  PGID STAT COMMAND 8073  8073 Ss   /bin/bash17121 17121 T    sh trap.sh17122 17121 T    sleep 60017123 17121 T    sleep 60017124 17121 T    sleep 60017143 17143 R+   ps -o pid,pgid,stat,args$Bring our test script to the foreground (fg) and interrupt (^C) again, this time choose not to ignore:$ fgsh trap.sh^CInterrupt: ignore? (y/n) [Y] >nTerminated$Check running processes, no more sleeping:$ ps -o pid,pgid,stat,args  PID  PGID STAT COMMAND 8073  8073 Ss   /bin/bash17159 17159 R+   ps -o pid,pgid,stat,args$ Note about your shell:I had to modify your code to get it to run on my system. You have #!/bin/sh as the first line in your scripts, yet the scripts use extensions (from bash or zsh) which are not available in /bin/sh.