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.
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) = ?