1

I have two scripts. script1 spawns script2 and then sends a SIGINT signal to it. However the trap in script2 doesn't seem to work?!

script1:

#!/bin/bash
./script2 &
sleep 1
kill -SIGINT $!
sleep 2

script2:

#!/bin/bash
echo "~~ENTRY"
trap 'echo you hit ctrl-c, waking up...' SIGINT
sleep infinity
echo "~~EXIT"

If change ./script2 & to ./script2 and press CTRL+C the whole things works fine. So what am I doing wrong?

Jolta
  • 2,620
  • 1
  • 29
  • 42
Pithikos
  • 18,827
  • 15
  • 113
  • 136

2 Answers2

2

You have several issues in your examples, at the end I have a solution for your issue:

  • your first script seems to miss a wait statement, thus, it exits after roughly 3 seconds. However script2 will remain in memory and running.

    How do you want bash to automatically figure which process it should send the SIGINT signal ?

  • Actually bash will disable SIGINT (and SIGQUIT) on background processes and they can't be enabled (you can check by running trap command alone to check the current status of set traps). See How to send a signal SIGINT from script to script ? BASH

    So your script2 is NOT setting a trap on SIGINT because it's a background process, both SIGINT and SIGQUIT are ignored and can't be anymore trapped nor resetted on background processes.

As a reference, here are the documentation from bash related to your issue:

Process group id effect on background process (in Job Control section of doc):

[...] processes whose process group ID is equal to the current terminal process group ID [..] receive keyboard-generated signals such as SIGINT. These processes are said to be in the foreground. Background processes are those whose process group ID differs from the terminal's; such processes are immune to keyboard-generated signals.

Default handler for SIGINT and SIGQUIT (in Signals section of doc):

Non-builtin commands run by bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.

and about modification of traps (in trap builtin doc):

Signals ignored upon entry to the shell cannot be trapped or reset.

SOLUTION 1

modify your script1 to be:

#!/bin/bash
{ ./script2; } &
sleep 1
subshell_pid=$!
pid=$(ps -ax -o ppid,pid --no-headers | sed -r 's/^ +//g;s/ +/ /g' |
                           grep "^$subshell_pid " | cut -f 2 -d " ")

kill -SIGINT $pid
sleep 2
wait      ## Don't forget this.

How does this work ? Actually, the usage of { and } will create a subshell, that will be limited by the explained limitation on SIGINT, because this subshell is a background process. However, the subshell's own subprocess are foreground and NOT background processes (for our subshell scope)... as a consequence, they can trap or reset SIGINT and SIGQUIT signals.

The trick is then to find the pid of this subprocess in the subshell, here I use ps to find the only process having the subshell's pid as parent pid.

SOLUTION 2

Actually, only direct new process managed as job will get their SIGINT and SIGQUIT ignored. A simple bash function won't. So if script2 code was in a function sourced in script1, here would be your new script1 that doesn't need anything else:

#!/bin/bash
script2() {
    ## script2 code
    echo "~~ENTRY"
    trap 'echo you hit ctrl-c, waking up...' SIGINT
    sleep infinity
    echo "~~EXIT"
}
## script1 code
script2 &
sleep 1
kill -SIGINT $!
sleep 2

This will work also. Behind the scene, the same mecanism than SOLUTION 1 is working: a bash function is very close to the { } construct.

Community
  • 1
  • 1
vaab
  • 9,685
  • 7
  • 55
  • 60
0

I guess what you are trying to achieve is that when script2 receives the SIGINT it continues and prints the message. Then, you need

#!/bin/bash
echo "~~ENTRY"
trap 'echo you hit ctrl-c, waking up...; CONT=true' SIGINT
CONT=false
while ! $CONT
do
   sleep 1
done
echo "~~EXIT"
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • It is much cleaner to do `sleep inifinity& wait` – William Pursell Aug 26 '14 at 18:11
  • By putting the sleep in the background, the shell will trap the signal immediately. (The sleep will continue running, and should be terminated in the trap.) – William Pursell Aug 26 '14 at 18:12
  • @WilliamPursell, as you mention `sleep/wait` can be used but I was assuming that something wants to be done while waiting – Diego Torres Milano Aug 26 '14 at 18:36
  • This doesn't work on my end. SIGINT is never caught by "./script2" and the trap message is never displayed, nor the "~~EXIT" message. "./script2" stays in memory and actually, this behavior seems consistent with bash documentation: background processes are NOT in the main process control group, and thus won't receive SIGINT by design. So, what did I miss ? – vaab Jul 29 '16 at 14:41