如何安全终止nohup运行的Python脚本以执行finally块
问题
我用nohup启动了一个Python脚本,脚本包含finally块,发生异常或触发KeyboardInterrupt时,会把队列中剩余条目打印到日志文件。在启动脚本的原Shell中操作一切正常:通过nohup后台启动脚本,用jobs查看进程,fg调回前台后按Ctrl+C,脚本会将队列待处理条目写入日志。但退出该Shell后,无法将进程调回前台发送KeyboardInterrupt;使用kill命令终止进程时,finally块不会执行,导致队列待处理条目丢失。请问有没有办法终止该nohup进程或发送类似KeyboardInterrupt的信号,同时让finally块执行代码?
更新:核心Python代码
try: cc = crawler.MultiThreadedWebCrawler(max_workers) cc.run_web_crawler() finally: cc.info(script_start_time) # 另一个脚本中info方法的定义: def info(self, script_start_time): print('\n', self.crawl_queue.qsize(), ' 条URL在crawl_queue中:\n') while self.crawl_queue.qsize() > 0: print(self.crawl_queue.qsize(), ' ', self.crawl_queue.get(), '\n') print("脚本启动时间: ", script_start_time) print("脚本结束时间: ", datetime.now().strftime('%Y-%m-%d__%H_%M_%S'))
解决方案
- 发送
SIGINT信号模拟Ctrl+C:先通过ps aux | grep 你的脚本文件名找到进程PID,然后执行kill -SIGINT <进程PID>。Python会把这个信号转换成KeyboardInterrupt异常,和前台按Ctrl+C效果一致,能触发finally块执行。 - 给脚本添加信号监听逻辑:如果默认
kill发的SIGTERM信号无法触发finally,可以在脚本开头添加信号处理代码,把SIGTERM转换成KeyboardInterrupt:
之后直接用import signal def handle_signal(signum, frame): raise KeyboardInterrupt # 监听SIGTERM和SIGINT信号 signal.signal(signal.SIGTERM, handle_signal) signal.signal(signal.SIGINT, handle_signal)kill <进程PID>就能触发finally块执行。 - 处理多线程场景的信号传递:因为你的脚本是多线程爬虫,要确保主线程能接收到信号。如果
run_web_crawler用threading启动线程,主线程不要被完全阻塞,同时线程最好设置为非守护线程(daemon=False),或者在finally块里主动终止线程并清理队列,避免线程还在运行时主线程退出。 - 禁止使用
kill -9:kill -9会强制杀死进程,Python没有任何机会执行清理代码,包括finally块,绝对不能用这个命令。
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

