繁体   English   中英

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

[英]How to terminate a python subprocess launched with shell=True

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

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

但是,当我尝试使用以下命令杀死时:

p.terminate()

要么

p.kill()

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

请注意,当我运行命令时:

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

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

使用进程组以便能够向组中的所有进程发送信号。 为此,您应该将会话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
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()应该工作。

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

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

我可以用

from subprocess import Popen

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

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

(在 Windows 上)

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

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

按照subprocess.Popen文档

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

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

在我的例子中,我在调用 proc.kill( proc.communicate() proc.kill() 这会清除进程标准输入、标准输出...并终止进程。

正如 Sai 所说,外壳是孩子,所以信号被它拦截——我发现的最好方法是使用 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() 应该按照您的预期工作。

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

    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)

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

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

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

p.terminate()

或者

p.kill()

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

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

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

发出p.terminate()时它确实成功终止。

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 ”信号,这将导致进程被杀死。

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

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

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

p.terminate()

或者

p.kill()

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

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

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

发出p.terminate()时它确实成功终止。

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

我试过 subprocess.Popen shell tr​​ue

  1. 子进程.Popen.kill
  2. 子进程.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):
        print(ExecutableProgram.stopProgram.__name__)
        sleep(.5)

        processName = self.program.name()

        for proc in psutil.process_iter():
            if processName in proc.name():
                parent_pid = proc.pid

        parent = psutil.Process(parent_pid)
        for child in parent.children(recursive=False): 
            child.kill()
        parent.kill(

你可以这样使用它:

from ExecutableProgram import ExecutableProgram


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

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

pkill 命令同时杀死父进程和所有子进程

os.system("pkill -TERM -P %s"%process.pid)

对我有用的解决方案

if os.name == 'nt':  # windows
    subprocess.Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
else:
    os.kill(process.pid, signal.SIGTERM)

完全成熟的解决方案将通过回调函数在达到超时或特定条件时终止正在运行的进程(包括子树)。 在撰写本文时适用于 Windows 和 Linux,从 Python 2.7 到 3.10。

使用pip install command_runner

超时示例:

from command_runner import command_runner

# Kills ping after 2 seconds
exit_code, output = command_runner('ping 127.0.0.1', shell=True, timeout=2)

具体条件示例:如果当前系统时间秒数 > 5,我们将停止 ping

from time import time
from command_runner import command_runner

def my_condition():
    # Arbitrary condition for demo
    return True if int(str(int(time()))[-1]) > 5

# Calls my_condition() every second (check_interval) and kills ping if my_condition() returns True
exit_code, output = command_runner('ping 127.0.0.1', shell=True, stop_on=my_condition, check_interval=1)

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

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

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

p.terminate()

或者

p.kill()

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

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

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

发出p.terminate()时它确实成功终止。

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

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

但是,当我尝试杀死使用:

p.terminate()

或者

p.kill()

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

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

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

发出p.terminate()时,它确实成功终止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM