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

Python脚本经Bash调用时无法正常处理SIGTERM信号的问题

问题分析与解决方案

1. 为什么两种调用方式行为不同?

这事儿核心就是时间差在搞鬼:

  • 当你在命令行手动启动Python进程再发信号时,中间有你手动输入命令的间隔,足够Python完成初始化——包括加载解释器、导入模块、注册好SIGTERM的自定义处理函数,之后才进入循环。这时候发信号,自然能被自定义逻辑捕获。
  • 但Bash脚本是自动连续执行的:刚把Python进程丢去后台,立刻就查PID发信号。这时候Python可能还在初始化流程里,压根没执行到signal.signal(signal.SIGTERM, sigterm_handler)这一行——自定义信号处理函数都没注册呢!此时收到SIGTERM,Python就会用默认逻辑直接终止,当然不会走你的延迟和输出流程。

这种跨系统跨Python版本都出现的现象,本质是Bash脚本的执行速度远快于Python解释器的启动初始化速度,这个时间差几乎是必然存在的。

2. 怎么让Bash调用时Python也能正常处理信号?

给你几个靠谱的解决办法,按易用性和可靠性排序:

方法一:给Python留足初始化时间(最简单)

在Bash脚本里发送信号前加个短暂延迟,确保Python已经注册好信号处理函数:

#!/bin/bash
./sigterm_tester.py &
child=$(pgrep -P $$)
sleep 1  # 等1秒,足够Python完成初始化
kill -15 $child
while true; do sleep 1; done

方法二:让Python主动通知Bash"我准备好了"(更可靠)

固定延迟有时候在极端环境下可能不够,不如让Python在完成信号注册后主动输出一个标记,Bash等这个标记出现再发信号:
首先修改Python脚本sigterm_tester.py,在注册信号后输出就绪标记(记得flush避免输出缓冲):

#!/usr/bin/env python3
import sys
import signal
import time

def sigterm_handler(signal, frame):
    time.sleep(5)
    print("dying")
    sys.exit()

signal.signal(signal.SIGTERM, sigterm_handler)

# 输出就绪标记,告诉Bash可以发信号了
print("ready", flush=True)

while True:
    time.sleep(1)

然后修改Bash脚本,等待这个标记:

#!/bin/bash
# 启动Python进程并记录PID(用$!比pgrep更可靠)
./sigterm_tester.py > /tmp/python_ready.log 2>&1 &
child_pid=$!

# 循环检查直到"ready"标记出现
while ! grep -q "ready" /tmp/python_ready.log; do
    sleep 0.1  # 短间隔轮询,避免浪费资源
done

# 现在可以放心发信号了
kill -15 $child_pid
while true; do sleep 1; done

方法三:用进程间通信确认(适合生产环境)

如果是严谨的生产场景,可以用管道、文件锁或者socket来实现Python和Bash的就绪确认,原理和方法二一致,但能避免文件IO的依赖,稳定性更高。

顺便提个小技巧:Bash里获取刚启动的后台进程PID,用$!比pgrep -P $$靠谱多了——pgrep可能会误匹配其他子进程,而$!直接返回最后一个后台进程的真实PID。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:26:34