Reputation: 823
Consider the following bash script s
:
#!/bin/bash
echo "Setting trap"
trap 'ctrl_c' INT
function ctrl_c() {
echo "CTRL+C pressed"
exit
}
sleep 1000
When calling it ./s
, it will sleep. If you press Ctrl+C it prints the message and exits.
Now call it, open another terminal and kill the corresponding bash pid with -INT flag. It does not work or do anything. Why?
Kill without flags works but does not call the ctrl_c() function.
Upvotes: 1
Views: 1509
Reputation: 490
Answer from @mark-plotnick explained the principal quite correct and clear. Further more, you can tweak your script just a little bit, without sending SIGINT to the process group:
#!/bin/bash
echo "Setting trap"
trap 'ctrl_c' INT
function ctrl_c() {
echo "CTRL+C pressed"
kill "${SLEEPER}"
exit
}
sleep 1000 &
SLEEPER="$?"
wait
In this script, sleep
is put to the background, then waiting for it to exit. So, Ctrl-C will be trapped in the main script as expected.
Upvotes: 0
Reputation: 10251
From the POSIX standard for the shell:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
To verify this, we can run strace
on the shell executing your script. Upon sending the SIGINT
from another terminal, bash just notes that the signal has been received and returns to waiting for its child, the sleep command, to finish:
rt_sigaction(SIGINT, {0x808a550, [], 0}, {0x80a4600, [], 0}, 8) = 0
waitpid(-1, 0xbfb56324, 0) = ? ERESTARTSYS
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = -1 EINTR (Interrupted system call)
waitpid(-1,
To make kill
have the same effect as Ctrl-C, you should send SIGINT
to the process group. The shell by default will put every process of a new command into its own pgrp:
$ ps -f -o pid,ppid,pgid,tty,comm -t pts/1
PID PPID PGID TT COMMAND
3460 3447 3460 pts/1 bash
29087 3460 29087 pts/1 \_ foo.sh
29120 29087 29087 pts/1 \_ sleep
$ kill -2 -29087
And now the trap runs and the shell exits:
waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 29120
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]}) = 29120
...
write(1, "CTRL+C pressed\n", 15) = 15
...
exit_group(0) = ?
Upvotes: 2
Reputation: 44344
sleep
is not a shell built-in:
$ which sleep
/bin/sleep
This means that when you run sleep
it will run as a separate child process. When a child process is created various things are copied from parent to child (inherited) and one of these is the signal mask. Note: only the mask. So if you had set:
trap '' INT
then sleep
would have inherited that - it means ignore the signal. However, in your case you expect sleep
(which is probably written in C) to execute a bash
function. No can do - wrong language for a start. Imagine: it would have to execute bash
to execute the bash function.
Caveat - details concerning signal handling can vary across platforms.
It is possible to make sleep
a builtin, see here but that's probably more trouble than it is worth.
Upvotes: -1
Reputation: 50
When we use the ps aux command to find the PID values, we see that there are 2 processes, this program and sleep. Program process does not interrupt with -INT parameter as you say. However, when the -2 or -INT (same meaning) parameter is used for the sleep process, the process is interrupted as expected. The message prints and exits.I did not understand why the main process did not stop, but I wanted to show that the sleep process can be interrupted by this method. I hope it will be good for you.
Upvotes: 0