如何让主进程捕获Ctrl-C?解决os.system()屏蔽KeyboardInterrupt问题
解决主进程捕获KeyboardInterrupt而非子进程的问题
问题原因
os.system()本质是调用系统shell执行命令,执行期间主进程会处于等待状态。当按下Ctrl-C产生SIGINT信号时,信号会被shell转发给它启动的子进程(也就是你调用的python3脚本),同时shell会自行处理该信号,不会将其传递给主进程,因此主进程无法触发KeyboardInterrupt。
解决方案:用subprocess替代os.system
subprocess模块提供了更精细的进程控制能力,能让主进程正常接收信号。下面分两种场景给出实现:
场景1:无需shell(推荐)
直接拆分命令为列表,用subprocess的参数处理输入输出重定向,避免依赖shell,同时更安全:
import subprocess import sys # 拆分命令为列表,避免shell解析 cmd = [sys.executable, sys.argv[1]] # 处理输入输出重定向 with open(sys.argv[2], 'r') as stdin_f, open('measure-temp-output', 'w') as stdout_f: try: # 等待子进程完成 subprocess.run(cmd, stdin=stdin_f, stdout=stdout_f, check=True) except KeyboardInterrupt: print("主进程捕获到Ctrl-C,执行中断处理逻辑") # 可根据需求添加子进程终止逻辑(若用Popen可直接调用terminate)
场景2:必须使用shell
如果你的命令依赖shell特性(比如复杂管道),可以开启shell=True,同时设置start_new_session=True让子进程处于新的会话组,这样SIGINT信号只会发送给主进程所在的组:
import subprocess import sys cmd = f"{sys.executable} {sys.argv[1]} < {sys.argv[2]} > measure-temp-output" try: # 启动子进程并设置新会话组 proc = subprocess.Popen(cmd, shell=True, start_new_session=True) proc.wait() except KeyboardInterrupt: print("主进程捕获到Ctrl-C") # 终止子进程并等待其退出 proc.terminate() proc.wait()
原理说明
- 用
subprocess.run()或Popen()时,主进程并非像os.system()那样完全挂起,而是在等待子进程期间仍能接收信号。 - 当
start_new_session=True时,子进程会创建新的会话组,Ctrl-C触发的SIGINT只会发送给主进程所在的会话组,确保主进程能捕获KeyboardInterrupt。
内容的提问来源于stack exchange,提问作者ArekBulski
相关产品推荐
相关产品推荐

