我有一个在 bash 4.3 中运行良好的脚本,但在 bash 3.2 中却给了我意想不到的行为。这是一个简化版本:
set -o errexit -o pipefail
task() {
local name=${1}
local duration=${2}
trap 'echo "[${SECONDS} secs] ${name}: SIGINT"; exit 255' INT
echo "[${SECONDS} secs] ${name}: Running"
sleep "${duration}"
echo "[${SECONDS} secs] ${name}: Done"
}
trap 'echo "[${SECONDS} secs] SIGINT"; exit 255' INT
task 'Task 1' 5 &
task 'Task 2' 5 &
wait
echo "[${SECONDS} secs] Done"
以下是使用 bash 4.3 (4.3.42(1)-release) 运行 CTRL-C 两秒后的输出:
[0 secs] Task 1: Running
[0 secs] Task 2: Running
^C[2 secs] SIGINT
[2 secs] Task 2: SIGINT
[2 secs] Task 1: SIGINT
prompt>
同样的事情,但对于 bash 3.2 (3.2.57(1)-release):
[0 secs] Task 1: Running
[0 secs] Task 2: Running
^C[2 secs] SIGINT
prompt> [5 secs] Task 2: Done
[5 secs] Task 1: Done
是否存在阻止上述脚本在 bash 3.2 下正常工作的已知问题?是否存在解决方法?
以下是我尝试过的一些事情:
父级中没有信号处理程序:
# bash 4.3 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C[2 secs] Task 2: SIGINT [2 secs] Task 1: SIGINT prompt> # bash 3.2 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C prompt> [5 secs] Task 2: Done [5 secs] Task 1: Done
根本没有信号处理程序:
# bash 4.3 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C prompt> # bash 3.2 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C prompt> [5 secs] Task 2: Done [5 secs] Task 1: Done
父级中的信号处理程序使用 SIGINT ( ) 杀死进程组
kill -INT -- -$$
:[0 secs] Task 1: Running [0 secs] Task 2: Running ^C[2 secs] SIGINT [2 secs] Task 2: SIGINT [2 secs] Task 1: SIGINT prompt> [0 secs] Task 1: Running [0 secs] Task 2: Running ^C[2 secs] SIGINT prompt> [5 secs] Task 2: Done [5 secs] Task 1: Done
父级中的信号处理程序使用 SIGTERM 终止进程组(任务捕获 SIGTERM):
# bash 4.3 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C[2 secs] SIGINT [2] 92813 terminated bash minimal_example.sh prompt> # bash 3.2 [0 secs] Task 1: Running [0 secs] Task 2: Running ^C[2 secs] SIGINT Terminated: 15 Terminated: 15 [2 secs] Task 2: SIGTERM [2 secs] Task 1: SIGTERM [1] 92836 terminated /bin/bash minimal_example.sh prompt>
最后一个是最接近在 3.2 中正常工作的,但相同的代码在 4.3 中的行为有所不同。
答案1
您可能是bash
经常影响make
用户的众所周知问题的受害者。
我还没有检查 bash 4,但bash 3
在脚本内错误地执行了作业控制。这通常会导致包含多个子目录循环的 makefile 不容易被杀死,^C
因为子进程在单独的进程组中运行,即使这些命令不是交互式命令。
smake
包括一个解决方法,/bin/sh
并将bash
SIGINT 显式转发到当前运行命令的进程组。但这是用C
.
由于没有标准的 UNIX 命令来检索子进程组,因此无法使用脚本中的常用 shell 来实现相同的功能。