如何终止使用shell=True启动的python子进程
我正在用以下命令启动一个子进程:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
但是,当我尝试用以下方式结束它时:
p.terminate()
或者
p.kill()
这个命令仍然在后台运行,所以我想知道我该如何真正终止这个进程。
需要注意的是,当我用以下命令运行时:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
在执行 p.terminate()
时,它确实能够成功终止。
11 个回答
103
如果你能使用 psutil 这个库,那么这个方法就能很好地工作:
import subprocess
import psutil
def kill(proc_pid):
process = psutil.Process(proc_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
proc = subprocess.Popen(["infinite_app", "param"], shell=True)
try:
proc.wait(timeout=3)
except subprocess.TimeoutExpired:
kill(proc.pid)
137
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()
p.kill()
会结束掉 shell 进程,但 cmd
仍然在运行。
我找到了一种方便的解决办法:
p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
这样做会让 cmd 继承 shell 进程,而不是让 shell 启动一个子进程,这样子进程就不会被杀掉。此时,p.pid
将是你的 cmd 进程的 ID。
这样的话,p.kill()
应该就能正常工作了。
不过我不太清楚这对你的管道会有什么影响。
546
使用一个 进程组,这样可以让你向组内的所有进程发送信号。为了做到这一点,你需要给你启动的子进程的父进程(在你的情况下是一个shell)附加一个 会话ID。这样,它就会成为这些进程的组长。当信号发送到进程组的组长时,这个信号会传递给这个组内的所有子进程。
下面是代码:
import os
import signal
import subprocess
# The os.setsid() is passed in the argument preexec_fn so
# it's run after the fork() and before exec() to run the shell.
pro = subprocess.Popen(cmd, stdout=subprocess.PIPE,
shell=True, preexec_fn=os.setsid)
os.killpg(os.getpgid(pro.pid), signal.SIGTERM) # Send the signal to all the process groups