You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何trap在bash与sh中的执行表现存在差异?

脚本在bash正常处理SIGINT但sh中失效的原因及解决办法

问题描述

编写了如下脚本,意图在父进程收到SIGINT(Ctrl+C)时,通过trap向后台子进程发送SIGINT信号,子进程捕获该信号并打印提示:

#!/bin/bash

first() {
    trap 'echo "INT first"' INT
    sleep 10
    echo "slept"
}

trap 'echo "killing"; kill -INT "$PID"; echo "kill sent"' INT

first &
PID=$!
echo "$PID"

wait

在bash中执行时,行为符合预期:

$ ./test.sh 
64257
^Ckilling
kill sent
INT first
slept

但将shebang改为#!/bin/sh后,子进程无法捕获SIGINT,父进程发送信号后无响应,需等待10秒才会打印slept:

$ ./test.sh 
64550
^Ckilling
kill sent

原因分析

差异源于POSIX标准与bash扩展的信号处理规则不同:

  • POSIX标准规定,非交互模式下的shell启动后台作业时,会自动将该作业的SIGINT和SIGQUIT信号设为忽略状态。大多数系统的/bin/sh(如dash)严格遵循这一标准,因此你的后台子进程默认忽略SIGINT,即使父进程发送该信号,子进程也不会触发自定义trap,sleep会继续运行至结束。
  • bash作为扩展,没有遵循这一POSIX规则,非交互模式下的后台作业不会自动忽略SIGINT,因此子进程的自定义trap可以正常捕获信号。

解决方案

要让脚本在sh中正常工作,需在子进程中显式恢复SIGINT的默认处理,取消忽略状态,再设置自定义trap。修改后的脚本如下:

#!/bin/sh

first() {
    # 恢复SIGINT的默认处理(取消shell自动设置的忽略状态)
    trap - INT
    # 设置自定义信号处理逻辑
    trap 'echo "INT first"' INT
    sleep 10
    echo "slept"
}

trap 'echo "killing"; kill -INT "$PID"; echo "kill sent"' INT

first &
PID=$!
echo "$PID"

wait

修改后在sh中执行,行为将与bash一致:按下Ctrl+C后,父进程触发trap发送信号,子进程捕获并打印INT first,sleep被中断后执行slept。

如果需要子进程收到SIGINT后直接退出(不打印slept),可以在自定义trap中添加exit:

trap 'echo "INT first"; exit' INT

内容的提问来源于stack exchange,提问作者Afkaaja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:47:42