为何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
相关产品推荐
相关产品推荐

