jam
jam

Reputation: 823

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.

Upvotes: 1

Views: 1509

Answers (4)

vivimice
vivimice

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

Mark Plotnick
Mark Plotnick

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

cdarke
cdarke

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

Hakan B.
Hakan B.

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

Related Questions