如何终止一个python子进程启动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()时成功终止。

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

正如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()应该按您期望的方式工作。

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

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

我可以用

from subprocess import Popen


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

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

(在Windows上)

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

这些答案都没有为我工作,所以我离开了工作的代码。在我的例子中,即使在用.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()。这将清除进程stdin, stdout…并且终止了这个过程。

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

    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)

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+ __abc1”;信号给正在运行的进程,什么会导致进程被终止。

这个方法对我很有效

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 < / p >

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)