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

如何终止使用shell=True启动的python子进程,python,linux,subprocess,kill-process,Python,Linux,Subprocess,Kill Process,我正在使用以下命令启动子流程: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) 但是,当我尝试使用以下方法杀人时: p.terminate() 或 该命令一直在后台运行,因此我想知道如何实际终止该进程 请注意,当我使用以下命令运行命令时: p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) 当发出p.terminate()时,它会成功终止shell=T

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

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
但是,当我尝试使用以下方法杀人时:

p.terminate()

该命令一直在后台运行,因此我想知道如何实际终止该进程

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

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

当发出
p.terminate()
时,它会成功终止
shell=True
shell是子进程,命令是它的子进程。因此,任何
SIGTERM
SIGKILL
都会杀死shell,但不会杀死它的子进程,我不记得有什么好的方法。
我能想到的最好的方法是使用
shell=False
,否则,当您杀死父shell进程时,它将留下一个失效的shell进程。

正如Sai所说,shell就是子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()应该按照您的预期工作。

使用a以便能够向组中的所有进程发送信号。为此,您应该将附加到派生/子进程的父进程,在您的示例中,它是一个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
p.kill()

我找到了一个方便的解决方法:

p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
这将导致cmd继承shell进程,而不是让shell启动一个子进程,子进程不会被终止
p.pid
将是cmd进程的id

p.kill()
应该可以工作

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

我可以用

from subprocess import Popen

process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
它杀死了
cmd.exe
和我发出命令的程序

(在Windows上)

如果您可以使用,则此功能非常有效:

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)

没有一个答案对我有效,所以我留下了有效的代码。在我的例子中,即使在使用
.kill()
终止进程并获得
.poll()
返回代码后,进程也没有终止

遵循
子流程.Popen

“…为了正确清理,行为良好的应用程序应该终止子进程并完成通信…”


在我的例子中,调用
proc.kill()
后,我丢失了
proc.communicate()
。这将清理进程stdin、stdout。。。我知道这是一个老问题,但这可能有助于寻找其他方法。这就是我在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中使用。

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

    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)

我觉得我们可以使用的:

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的进程我没有看到这里提到的,所以我把它放在那里,以防有人需要它。如果您只想确保子流程成功终止,那么可以将其放在上下文管理器中。例如,我希望我的标准打印机打印一个输出图像,并使用上下文管理器确保子进程终止

导入子流程
将open(filename,'rb')作为f:
img=f.read()
将subprocess.Popen(“/usr/bin/lpr”,stdin=subprocess.PIPE)作为lpr:
lpr.stdin.write(img)
打印('打印图像…')

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

对于
Python 3.5
或+(实际上是在
Python 3.8
上测试的)有一种非常简单的方法

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

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

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

这在Pyhton 3.8.10 Win 10上对我有效

我尝试了子进程。Popenshell true

  • subprocess.Popen.kill
  • subprocess.Popen.terminate
  • 但这是真的

    不要使用os.kill()

    这是你如何使用它的:

    from ExecutableProgram import ExecutableProgram
    
    
    program = ExecutableProgram(r"<your executable>")
    
    program.startProgram()
    input()
    program.stopProgram()
    
    
    从可执行程序导入可执行程序
    程序=可执行程序(r“”)
    program.startProgram()
    输入()
    program.stopProgram()
    
    在我的例子中,由于cmd是“cd path&&zsync etc”,所以它并没有真正的帮助。因此,这实际上使命令失败!使用绝对路径而不是更改目录。。。(可选)将os.chdir(…)添加到该目录中…内置了更改子进程工作目录的功能。只需将
    cwd
    参数传递给即可。我使用了shlex,但问题仍然存在,kill并不是终止子进程。
    subprocess.CREATE_NEW_PROCESS_GROUP
    与此有何关系?我们的测试建议设置ID!=setpgid,并且该os.pgkill仅终止仍具有相同进程组id的子进程。已更改进程组的进程不会终止,即使它们可能仍具有相同的会话id。@PiotrDobrogost:我不建议执行os.setpid(),因为它还有其他影响。其中,它断开了控制TTY的连接,使新流程成为流程组长。看看在Windows中如何执行此操作
    setsid
    仅在*nix系统上可用。您的
    cmd
    看起来像什么?它可能包含一个命令,该命令触发要启动的多个进程。因此,不清楚您谈论的是哪个进程。相关:没有
    shell=True
    会有很大的区别吗?
    AttributeError:“进程”对象没有att
    
    import os
    import signal
    import subprocess
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    
    os.killpg(os.getpgid(pro.pid), signal.SIGINT)
    
    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
    
    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()