bashshellunixsignalskill

Kill -INT not working on bash script with trap INT set


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.


Solution

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