Python subprocess调用growlnotify后子进程无法退出的解决方法
问题根因
你的代码存在5个核心问题,导致进程残留:
- 杀错进程对象:开启
shell=True时,Popen启动的是sh/bash shell进程,返回的PID属于shell而非growlnotify。你调用kill()/terminate()只会终止外层shell,真正的growlnotify进程会变成孤儿进程被系统init进程接管,持续驻留不会被回收。 - 参数逻辑错误:
--sticky参数的作用是让通知永久停留在屏幕上,直到用户手动点击关闭。对应growlnotify进程会一直运行等待通知关闭事件,本身就不会自动退出。 - 资源回收缺失:就算成功终止子进程,没有调用
wait()读取子进程退出状态,会导致子进程成为僵尸进程,持续占用系统进程表资源。 - 命令拼接风险:用f-string拼接shell命令存在命令注入漏洞,只要title、message等参数包含单引号或shell特殊字符,就会导致命令执行失败,甚至执行恶意代码。
- 无效逻辑冗余:原代码中
application参数后拼接了无意义的多余空格,会导致growlnotify识别应用名异常;10秒固定sleep是硬编码的无意义等待,完全可以用进程等待机制替代。
修正方案
根据你是否需要通知驻留,分两种场景实现,所有子进程都会被自动回收,无残留:
import subprocess import os def growl_request(application, hosts, password, message, title): for host in hosts: cmd = [ "/usr/local/bin/growlnotify", # 不需要通知永久驻留就删除下一行的--sticky,进程发完通知会自动退出 "--sticky", "--title", title, "--application", application.strip(), "--host", host, "--password", password, message ] # 启动子进程,放到独立进程组,避免后续杀进程漏子进程 proc = subprocess.Popen( cmd, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) try: # 等待进程自动退出,超时时间可根据网络情况调整,替代硬sleep proc.wait(timeout=10) except subprocess.TimeoutExpired: # 超时后给整个进程组发终止信号,保证growlnotify进程被杀死 os.killpg(os.getpgid(proc.pid), subprocess.SIGTERM) try: proc.wait(timeout=3) except subprocess.TimeoutExpired: # 进程没响应SIGTERM就强制发SIGKILL os.killpg(os.getpgid(proc.pid), subprocess.SIGKILL) proc.wait()
注意:如果不需要通知常驻屏幕,直接删除
--sticky参数即可,growlnotify完成通知推送后会立刻自动退出,不需要任何手动kill操作,整个流程耗时只有网络传输的时间,没有多余等待。
如果你必须保留--sticky让通知常驻,上面的代码会在10秒超时后彻底终止整个growlnotify进程组,不会留下任何孤儿进程,同时wait()调用会自动回收子进程所有资源,不会产生僵尸进程。
内容的提问来源于stack exchange,提问作者ILikeTurtles
相关产品推荐
相关产品推荐

