在 bash 脚本中,信号被捕获时会发生什么

In bash script, what happens when a signal is trapped

假设我运行将以下示例另存为hello.sh

#!/bin/bash
hello() {
    echo "hello"
}

trap "hello" INT
sleep 100
echo "end"

然后在 shell 我开始 hello.sh。 1 秒后我按下 Ctrl-C.

请将 sleep 视为我启动的任何长 运行 进程。

这是我的问题:

  1. 生成SIGINT时,是直接传递给sleep还是传递给shell脚本?
  2. 如果是第二个,我可以让sleep有机会处理SIGINT而不传播到它的父hello.sh吗?
  3. 如果是第一个,hello函数结束后sleep的状态是什么?

我的测试让我觉得下面的事情是按顺序发生的

  1. sleep 开始 运行
  2. hello.sh收到信号
  3. 函数hello开始运行
  4. 函数hello完成
  5. echo "end" 开始 运行
  6. 脚本退出。

但是 sleep 进程在哪个阶段退出以及由于什么原因(例如 SIGINT 停止了 sleep 进程?)

$ cat foo.sh
_sigint()
{
    echo "received SIGINT"
}

trap _sigint INT

sleep 10000
echo "status=$?"
$ bash foo.sh              # <-- start foo.sh and then press CTRL-C
^Creceived SIGINT
status=130

一些解释:

  • sleep 作为 bash 的子进程运行。 bashsleep 运行 在同一个 进程组 中,现在是 前台进程组

  • 当按下CTRT-C时,会产生SIGINT,信号会传送到 前台进程组中的所有进程因此bashsleep都将收到SIGINT.

  • 根据bashmanual(也可以看man bash

    If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes.

    所以这里 bash 会等待 sleep 被杀死 (默认的 SIGINT 行为是终止进程。参见 signal(7) 首先然后 bash 运行它的 SIGINT 陷阱。

  • 根据bash手册

    The return value of a simple command is its exit status, or 128+n if the command is terminated by signal n.

    SIGINT 是 2 (参见 kill -l 所以 sleep 的退出状态是 128+2=130.