我正在使用以下命令启动子流程:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
但是,当我尝试杀死使用:
p.terminate()
要么
p.kill()
该命令一直在后台运行,所以我想知道如何才能真正终止该过程。
请注意,当我使用以下命令运行命令时:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
发出p.terminate()时,它确实成功终止。
您的cmd是什么样的? 它可能包含触发多个进程启动的命令。 因此,目前尚不清楚您在谈论哪个过程。
相关:Python:当父母去世时,如何杀死子进程?
shell=True没有很大的不同吗?
使用进程组,以便能够向组中的所有进程发送信号。为此,您应该将会话ID附加到生成的子进程的父进程中,在您的情况下这是一个外壳程序。这将使其成为流程的组长。因此,现在,当信号发送到流程组负责人时,它将被传输到该组的所有子流程。
这是代码:
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
signal.SIGUSR1是错字吗?
preexec_fn=os.setpgrp也可以
在Windows上可以使用吗?
@PiotrDobrogost:不幸的是,因为os.setsid在Windows中不可用(docs.python.org/library/os.html#os.setsid),我不知道这是否有帮助,但是您可以在这里查看(bugs.python。 org / issue5115)。
subprocess.CREATE_NEW_PROCESS_GROUP与这有何关系?
@PiotrDobrogost:很好找到:),显然,如果您使用subprocess.CREATE_NEW_PROCESS_GROUP标志,则可以在Window中创建一个进程组,在此处检查操作方式:hg.python.org/cpython/file/321414874b26/Lib/test/,可惜我没有我手里有一台Windows机器,所以请您试一下让我知道吗? :)
运行python -c"import subprocess; subprocess.Popen([ping, -t, google.com], shell=True).terminate()"将杀死该子进程。但是我认为它与subprocess.CREATE_NEW_PROCESS_GROUP无关,因为它没有在subprocess.py中的任何位置设置。除了根据文档,GenerateConsoleCtrlEvent函数使用进程组来启用向一组控制台进程发送CTRL + BREAK信号。并且Popen.terminate()不发送任何信号,而是调用TerminateProcess() Windows API函数。
我们的测试建议setid!= setpgid,而os.pgkill只杀死仍然具有相同进程组ID的子进程。更改了进程组的进程不会被杀死,即使它们可能仍具有相同的会话ID ...
@PiotrDobrogost:CREATE_NEW_PROCESS_GROUP可用于在Windows上模拟start_new_session=True
即使shell = False,这也有效。 hwjp的发现仍然适用。
我不建议您使用os.setsid(),因为它还有其他作用。其中,它断开了控制TTY的连接,并使新过程成为过程组负责人。参见win.tue.nl/~aeb/linux/lk/lk-10.html
@parasietje:这不是这种方法的重点吗?要创建一个新的进程组,在其中可以用一个信号杀死所有进程?我在启动无法终止的新流程的过程中遇到问题。这个答案解决了我的问题。
setsid创建一个新会话,而setpgrp仅在没有会话时创建一个新会话。如果嵌套多个脚本,则最外面的应该调用setid,而所有其他的应该调用setpgrp,否则内部的脚本将再次成为init的父对象,并且不会自动被杀死。 en.wikipedia.org/wiki/Process_group#Details
您将如何在Windows中执行此操作? setsid仅在* nix系统上可用。
如果os.setsid()会因errno.EPERM(错误号1)而失败,例如在GUI终端会话中运行脚本,这会失败吗?还是因为它发生在fork()之后但exec()之前而起作用?
打印Terminated输出,如何防止打印? @mouad
如果我没有shell=True,答案会改变多少?
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()应该可以工作。
我不知道这会对您的管道产生什么影响。
这在Windows和MAC上有效吗?
* nix不错的解决方案,谢谢!在Linux上可以使用,在Mac上也可以使用。
非常好的解决方案。如果您的cmd恰好是其他程序的shell脚本包装,请也使用exec调用最终二进制文件,以便只有一个子进程。
这很漂亮。我一直在尝试找出如何在Ubuntu上为每个工作区生成并杀死一个子进程。这个答案帮助了我。希望我可以多次投票
如果在cmd中使用了分号,则此方法不起作用
@speedyrazor-在Windows10上不起作用。我认为应该明确标出具体答案。
如果可以使用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)
最终在Windows上为我工作。
pip install psutil的AttributeError: Process object has no attribute get_children。
我认为get_children()应该是children()。但是它在Windows上对我不起作用,该过程仍然存在。
@Godsmith-psutil API发生了变化,您的权利是:children()与过去的get_children()一样。如果它在Windows上不起作用,那么您可能要在GitHub中创建一个错误票证
我可以用
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
它杀死了cmd.exe和我给出命令的程序。
(在Windows上)
这适用于Windows。谢谢!
当shell=True时,shell是子进程,命令是其子进程。因此,任何SIGTERM或SIGKILL都将杀死shell,但不会杀死其子进程,而且我不记得这样做的好方法。
我能想到的最好的方法是使用shell=False,否则当您杀死父shell进程时,它将留下一个已失效的shell进程。
这些答案都没有对我有用,所以我留下了行之有效的代码。就我而言,即使使用.kill()终止了进程并获得了.poll()返回代码,该进程也没有终止。
遵循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()
就我而言,在调用proc.kill()之后我错过了proc.communicate()。这将清理进程stdin,stdout ...,并终止进程。
此解决方案在linux和python 2.7中对我不起作用
@xyz在Linux和python 3.5中确实为我工作。检查python 2.7的文档
@espinal,谢谢,是的。它可能是Linux问题。在Raspberry 3上运行的Raspbian Linux
正如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()应该可以按照您的期望工作。
在我的情况下,鉴于cmd是" cd path && zsync etc etc",它实际上并没有帮助。这样实际上会使命令失败!
使用绝对路径而不是更改目录...可选os.chdir(...)到该目录...
内置了更改子进程的工作目录的功能。只需将cwd参数传递给Popen。
我使用了shlex,但问题仍然存在,kill并不是杀死子进程。
我觉得我们可以使用的是:
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的过程
我在这里没有看到此内容,因此我将其放在那里,以防有人需要。如果您要做的只是确保子流程成功终止,则可以将其放在上下文管理器中。例如,我希望我的标准打印机打印出一张图像,并使用上下文管理器确保子进程终止。
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...')
我相信这种方法也是跨平台的。
我看不到这里的代码如何终止进程。你能澄清一下吗?
我已经清楚地说过,如果您想做的所有事情都可以确保在继续执行代码的下一行之前您的进程已终止,则可以使用此方法。我没有声称它会终止该过程。
将信号发送到组中的所有进程
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)
我知道这是一个古老的问题,但这可能会对寻求其他方法的人有所帮助。这就是我在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中使用。