Linux 如何终止使用 shell=True 启动的 python 子进程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4789837/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:13:11  来源:igfitidea点击:

How to terminate a python subprocess launched with shell=True

pythonlinuxsubprocesskill-process

提问by user175259

I'm launching a subprocess with the following command:

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

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

However, when I try to kill using:

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

p.terminate()

or

或者

p.kill()

The command keeps running in the background, so I was wondering how can I actually terminate the process.

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

Note that when I run the command with:

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

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

It does terminate successfully when issuing the p.terminate().

发出p.terminate().

采纳答案by mouad

Use a process groupso as to enable sending a signal to all the process in the groups. For that, you should attach a session idto the parent process of the spawned/child processes, which is a shell in your case. This will make it the group leader of the processes. So now, when a signal is sent to the process group leader, it's transmitted to all of the child processes of this group.

使用进程组以便能够向中的所有进程发送信号。为此,您应该将会话ID附加到衍生/子进程的父进程,在您的情况下它是一个 shell。这将使它成为流程的组长。所以现在,当一个信号被发送到进程组领导时,它被传输到这个组的所有子进程。

Here's the code:

这是代码:

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

回答by Sai Venkat

When shell=Truethe shell is the child process, and the commands are its children. So any SIGTERMor SIGKILLwill kill the shell but not its child processes, and I don't remember a good way to do it. The best way I can think of is to use shell=False, otherwise when you kill the parent shell process, it will leave a defunct shell process.

shell=Trueshell 是子进程并且命令是它的子进程时。所以 any SIGTERMorSIGKILL会杀死shell而不是它的子进程,我不记得有什么好的方法来做到这一点。我能想到的最好的方法是使用shell=False,否则当你杀死父shell进程时,它会留下一个不存在的shell进程。

回答by Matt Billenstein

As Sai said, the shell is the child, so signals are intercepted by it -- best way I've found is to use shell=False and use shlex to split the command line:

正如 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)

Then p.kill() and p.terminate() should work how you expect.

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

回答by Bryant Hansen

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

p.kill()ends up killing the shell process and cmdis still running.

p.kill()最终杀死shell进程并且cmd仍在运行。

I found a convenient fix this by:

我通过以下方式找到了一个方便的解决方法:

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

This will cause cmd to inherit the shell process, instead of having the shell launch a child process, which does not get killed. p.pidwill be the id of your cmd process then.

这将导致 cmd 继承 shell 进程,而不是让 shell 启动一个不会被杀死的子进程。 p.pid那么将是您的 cmd 进程的 ID。

p.kill()should work.

p.kill()应该管用。

I don't know what effect this will have on your pipe though.

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

回答by SPratap

I could do it using

我可以用

from subprocess import Popen

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

it killed the cmd.exeand the program that i gave the command for.

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

(On Windows)

(在 Windows 上)

回答by Jovik

If you can use psutil, then this works perfectly:

如果您可以使用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)

回答by epinal

None of this answers worked for me so Im leaving the code that did work. In my case even after killing the process with .kill()and getting a .poll()return code the process didn't terminate.

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

Following the subprocess.Popendocumentation:

按照subprocess.Popen文档

"...in order to cleanup properly a well-behaved application should kill the child process and finish communication..."

“......为了正确清理,一个行为良好的应用程序应该杀死子进程并完成通信......”

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

In my case I was missing the proc.communicate()after calling proc.kill(). This cleans the process stdin, stdout ... and does terminate the process.

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

回答by Zx4161

I know this is an old question but this may help someone looking for a different method. This is what I use on windows to kill processes that I've called.

我知道这是一个老问题,但这可能有助于寻找不同方法的人。这就是我在 Windows 上用来杀死我调用的进程的方法。

si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)

/IM is the image name, you can also do /PID if you want. /T kills the process as well as the child processes. /F force terminates it. si, as I have it set, is how you do this without showing a CMD window. This code is used in python 3.

/IM 是图像名称,如果需要,您也可以使用 /PID。/T 杀死进程以及子进程。/F force 终止它。si,正如我所设置的,是你如何在不显示 CMD 窗口的情况下执行此操作。此代码用于 python 3。

回答by rogers.wang

Send the signal to all the processes in group

向组内所有进程发送信号

    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)

回答by Nilesh K.

what i feel like we could use:

我觉得我们可以使用:

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

os.killpg(os.getpgid(pro.pid), signal.SIGINT)

this will not kill all your task but the process with the p.pid

这不会终止您的所有任务,而是使用 p.pid 终止进程