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

为何Ctrl-C终止Shell脚本的行为存在差异?

为什么Ctrl-C在两种Shell脚本场景下的终止行为不同?

先来看你遇到的两个场景:

场景一:直接执行sleep infinity

#!/usr/bin/env bash
sleep infinity
# 按下Ctrl-C时,sleep和脚本均终止,看不到"End"输出
echo End

行为:按下Ctrl-C后,整个脚本直接停掉,echo End完全没机会执行。

场景二:通过docker exec执行sleep infinity

#!/usr/bin/env bash
docker exec container-id sleep infinity
# 按下Ctrl-C时,docker exec停止,但脚本继续执行,能看到"End"输出
echo End

行为:按下Ctrl-C后,docker exec进程终止,脚本却继续跑完,echo End正常输出。


核心原因:进程组与信号传递的差异

要搞懂这个区别,得先明白Ctrl-C到底干了什么:当你在终端按下Ctrl-C时,终端会给当前前台进程组的所有进程发送一个SIGINT(中断信号),这个信号的默认行为就是让进程直接终止。

场景一的逻辑:同组进程共享信号

脚本启动sleep后,sleep和脚本本身属于同一个前台进程组——相当于它们是绑定在一起的"队友"。当终端发送SIGINT时:

  • sleep收到信号直接终止
  • 脚本也收到了同样的SIGINT信号,按照默认规则直接停止运行,自然就没机会执行后面的echo End了。

场景二的逻辑:docker exec的进程组隔离

docker exec的特殊之处在于,它启动后会做一个关键操作:创建新的进程组,把自己和容器内的sleep进程放到这个新组里,脱离了脚本所在的原进程组。

这就导致:当你按下Ctrl-C时,终端只会把SIGINT发送给docker exec所在的新进程组,脚本所在的原进程组根本收不到这个信号。

  • docker exec收到SIGINT后,会把信号转发给容器内的sleep进程,然后自己正常退出
  • 脚本发现子进程docker exec已经跑完了,就继续执行后续的echo End命令。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 03:22:40