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

