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

如何正确终止通过subprocess运行的后台进程?

问题根源

你在subprocess.Popen()的参数里加了"&",这会让bash启动my_script.sh后立刻把脚本放到后台运行,随后bash进程自己直接退出。此时你拿到的process对象对应的是已经结束的bash父进程,调用kill()或terminate()自然对真正在后台运行的my_script.sh进程无效。

最佳解决办法

方案1:移除&,让subprocess直接管理进程

subprocess.Popen()本身就是异步启动进程的,不需要借助bash的&来后台运行。修改代码如下:

def my_method():
    try:
        # 移除最后的"&"参数
        process = subprocess.Popen(["bash", "my_script.sh"],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        )
    except Exception as e:
        print(f"Error running the script: {e}")
    return process

def caller():
    process = my_method()
    # 优雅终止进程
    process.terminate()
    # 若需要强制杀死,用kill()
    # process.kill()

此时process对象直接对应运行my_script.sh的bash进程,调用terminate()或kill()会直接终止该bash进程,连带终止脚本(如果脚本没有自行fork独立子进程的话)。

方案2:使用进程组杀死所有关联进程

如果脚本本身会fork独立的后台子进程,仅终止bash父进程无法完全清理,可通过创建新进程组的方式,一次性杀死整个组内的所有进程:

import os
import signal

def my_method():
    try:
        # 添加start_new_session=True创建新进程组
        process = subprocess.Popen(["bash", "my_script.sh"],
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        start_new_session=True
                        )
    except Exception as e:
        print(f"Error running the script: {e}")
    return process

def caller():
    process = my_method()
    try:
        # 获取进程组ID,杀死整个进程组
        pgid = os.getpgid(process.pid)
        os.killpg(pgid, signal.SIGTERM)  # 优雅终止
        # 强制杀死用下面的语句
        # os.killpg(pgid, signal.SIGKILL)
    except ProcessLookupError:
        # 进程已提前退出,忽略即可
        pass

这种方式能确保所有属于该脚本的关联进程都被终止,适合复杂的脚本场景。

方案3:记录脚本PID(不推荐)

如果你一定要保留&的写法,可在my_script.sh中写入自身PID到文件,再通过Python读取PID并杀死进程:
修改my_script.sh:

# 脚本开头添加PID记录
echo $$ > /tmp/my_script.pid
# 原有脚本逻辑...

Python代码:

import os
import signal

def caller():
    process = my_method()
    try:
        with open('/tmp/my_script.pid', 'r') as f:
            script_pid = int(f.read().strip())
        os.kill(script_pid, signal.SIGTERM)
    except (FileNotFoundError, ProcessLookupError):
        pass

此方式存在PID文件被覆盖、误杀其他进程的风险,仅作为特殊场景的备选方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:08:30