385

我正在使用以下命令启动一个子进程:

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)

但是,当我尝试使用以下方法杀死时:

p.terminate()

或者

p.kill()

该命令一直在后台运行,所以我想知道如何才能真正终止该进程。

请注意,当我使用以下命令运行命令时:

p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

它在发出p.terminate().

4

10 回答 10

487

使用进程组以便能够向组中的所有进程发送信号。为此,您应该将会话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
于 2011-01-25T09:07:49.523 回答
116
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()应该管用。

我不知道这会对你的管道产生什么影响。

于 2012-10-30T16:01:25.313 回答
76

If you can use psutil, then this works perfectly:

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)
于 2014-08-05T09:07:15.487 回答
34

我可以使用

from subprocess import Popen

process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))

它杀死了cmd.exe我发出命令的程序。

(在 Windows 上)

于 2013-07-12T12:24:49.113 回答
16

shell=Trueshell 是子进程时,命令是它的子进程。所以任何SIGTERMorSIGKILL都会杀死外壳,但不会杀死它的子进程,我不记得有什么好方法来做到这一点。我能想到的最好的方法是使用shell=False,否则当你杀死父 shell 进程时,它会留下一个失效的 shell 进程。

于 2011-01-25T04:22:40.673 回答
11

这些答案都不适合我,所以我留下了有效的代码。在我的情况下,即使在终止进程.kill()并获得.poll()返回码之后,进程也没有终止。

按照subprocess.Popen 文档

“......为了正确清理,一个表现良好的应用程序应该终止子进程并完成通信......”

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

就我而言,我错过了proc.communicate()after call proc.kill()。这会清除进程 stdin、stdout ... 并终止进程。

于 2018-02-28T21:22:09.487 回答
7

正如 Sai 所说,shell 是孩子,所以信号被它拦截——我发现最好的方法是使用 shell=False 并使用 shlex 来拆分命令行:

if isinstance(command, unicode):
    cmd = command.encode('utf8')
args = shlex.split(cmd)

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

然后 p.kill() 和 p.terminate() 应该按照您的预期工作。

于 2011-01-25T04:53:19.237 回答
3

向组中的所有进程发送信号

    self.proc = Popen(commands, 
            stdout=PIPE, 
            stderr=STDOUT, 
            universal_newlines=True, 
            preexec_fn=os.setsid)

    os.killpg(os.getpgid(self.proc.pid), signal.SIGHUP)
    os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM)
于 2018-11-05T06:31:23.057 回答
1

Python 3.5or +有一个非常简单的方法(实际测试过Python 3.8

import subprocess, signal, time
p = subprocess.Popen(['cmd'], shell=True)
time.sleep(5) #Wait 5 secs before killing
p.send_signal(signal.CTRL_C_EVENT)

然后,如果你有键盘输入检测,你的代码可能会在某个时候崩溃,或者像这样。在这种情况下,在给出错误的代码/函数行上,只需使用:

try:
    FailingCode #here goes the code which is raising KeyboardInterrupt
except KeyboardInterrupt:
    pass

这段代码所做的只是向正在运行的进程发送一个“ CTRL+ C”信号,这会导致进程被杀死。

于 2020-11-28T11:38:58.943 回答
0

对我有用的解决方案

if os.name == 'nt':  # windows
    subprocess.Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
else:
    os.kill(process.pid, signal.SIGTERM)
于 2022-01-31T11:46:36.007 回答