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

时间:2011-01-25 03:58:44

标签: python linux subprocess kill-process

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

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

然而,当我尝试使用时杀死:

p.terminate()

p.kill()

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

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

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

发出p.terminate()时会成功终止。

14 个答案:

答案 0 :(得分:360)

使用process group以便启用向组中的所有进程发送信号。为此,您应该将session id附加到衍生/子进程的父进程,这是您的情况下的shell。这将使其成为流程的组长。所以现在,当一个信号发送给进程组负责人时,它会被传输到该组的所有子进程。

以下是代码:

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

答案 1 :(得分:81)

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()应该有用。

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

答案 2 :(得分:39)

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

答案 3 :(得分:20)

我可以用

来做
from subprocess import Popen

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

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

(在Windows上)

答案 4 :(得分:8)

shell=True shell是子进程时,命令是其子进程。所以任何SIGTERMSIGKILL都会杀死shell而不是它的子进程,我不记得一个好方法。  我能想到的最好的方法是使用shell=False,否则当你杀死父shell进程时,它会留下一个已经失效的shell进程。

答案 5 :(得分:6)

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

关注subprocess.Popen documentation

  

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

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

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

答案 6 :(得分:5)

正如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()应该如你所愿。

答案 7 :(得分:1)

我觉得我们可以使用:

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

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

这不会杀死您的所有任务,而是杀死带有p.pid的进程

答案 8 :(得分:0)

我知道这是一个古老的问题,但这可能会对寻求其他方法的人有所帮助。这就是我在Windows上用来杀死已调用进程的方法。

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

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

答案 9 :(得分:0)

将信号发送到组中的所有进程

    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)

答案 10 :(得分:0)

我在这里没有看到此内容,因此我将其放在此处,以防有人需要。如果您要做的只是确保子流程成功终止,则可以将其放在上下文管理器中。例如,我希望我的标准打印机打印出一张图像,并使用上下文管理器确保子进程终止。

import subprocess

with open(filename,'rb') as f:
    img=f.read()
with subprocess.Popen("/usr/bin/lpr", stdin=subprocess.PIPE) as lpr:
    lpr.stdin.write(img)
print('Printed image...')

我相信这种方法也是跨平台的。

答案 11 :(得分:0)

Python 3.5或+(实际上是在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 ”信号,这将导致该进程被杀死。

答案 12 :(得分:0)

这在 Pyhton 3.8.10 Win 10 上对我有用。

我试过 subprocess.Popen shell tr​​ue

  1. subprocess.Popen.kill
  2. subprocess.Popen.terminate

但这确实https://stackoverflow.com/a/4789957/8518485

不要使用 os.kill()。

import subprocess

class ExecutableProgram:
    program = None

    def __init__(self,path):
        self.path = path
        
    
    def startProgram(self):
        self.program = subprocess.Popen(self.path)


    def stopProgram(self):
        self.program.kill()

这就是你如何使用它:

from ExecutableProgram import ExecutableProgram


program = ExecutableProgram(r"<your executable>")

program.startProgram()
input()
program.stopProgram()

答案 13 :(得分:-1)

尽管这是一个老问题,但它在Google中的排名很高。因此,我决定发布一种答案,提出一种新方法,有人可以在python 3中使用它来轻松,自信地进行管理。从python 3.5开始,您已经在subprocess包中添加了一个名为run()的新方法。

documentation说:

对于它可以处理的所有用例,建议使用此方法来调用子流程。对于更高级的用例,可以直接使用基础的Popen接口。

subprocess.run():

运行args描述的命令。等待命令完成,然后返回一个CompletedProcess实例。

例如,可以在python控制台中运行此代码段:

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

P.S。对于OP的特定问题,我无法重现他的问题。我使用popen()运行的命令正确终止。