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

use*_*259 285 python linux subprocess kill-process

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

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
Run Code Online (Sandbox Code Playgroud)

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

p.terminate()
Run Code Online (Sandbox Code Playgroud)

要么

p.kill()
Run Code Online (Sandbox Code Playgroud)

该命令在后台继续运行,所以我想知道如何实际终止该进程.

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

p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
Run Code Online (Sandbox Code Playgroud)

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

mou*_*uad 391

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

  • 我们的测试建议使用setsid!= setpgid,并且os.pgkill只会杀死仍然具有相同进程组ID的子进程.已更改进程组的进程未被杀死,即使它们仍可能具有相同的会话ID ... (11认同)
  • `subprocess.CREATE_NEW_PROCESS_GROUP`与此有何关系? (6认同)
  • 我不建议做os.setsid(),因为它也有其他效果.其中,它断开控制TTY并使新进程成为进程组的领导者.见http://www.win.tue.nl/~aeb/linux/lk/lk-10.html (4认同)
  • @PiotrDobrogost:[`CREATE_NEW_PROCESS_GROUP`可用于在Windows上模拟`start_new_session = True`](http://stackoverflow.com/a/13256908/4279) (3认同)
  • @PiotrDobrogost:可悲的是,因为``os.setsid``在windows中不可用(http://docs.python.org/library/os.html#os.setsid),我不知道这是否有帮助但你可以在这里(http://bugs.python.org/issue5115)查看有关如何做到这一点的一些见解. (2认同)
  • setsid创建一个新会话,而setpgrp仅在没有会话时创建一个新会话。如果嵌套多个脚本,则最外面的应该调用setid,而所有其他的应该调用setpgrp,否则内部的脚本将再次成为init的父对象,并且不会自动被杀死。https://zh.wikipedia.org/wiki/Process_group#详细信息 (2认同)
  • 你会如何在Windows中执行此操作?`setsid`仅适用于*nix系统. (2认同)

小智 85

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()
Run Code Online (Sandbox Code Playgroud)

p.kill()最终杀死shell进程并cmd仍在运行.

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

p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
Run Code Online (Sandbox Code Playgroud)

这将导致cmd继承shell进程,而不是让shell启动子进程,而不会被杀死. p.pid那将是你的cmd进程的id.

p.kill() 应该管用.

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

  • @speedyrazor - 不适用于 Windows10。我认为操作系统的具体答案应该明确标记。 (6认同)
  • 这适用于Windows和MAC吗? (2认同)
  • *nix 的又好又轻的解决方案,谢谢!适用于 Linux,也应该适用于 Mac。 (2认同)
  • 这很漂亮。我一直在试图弄清楚如何在 Ubuntu 上为每个工作区生成和终止一个子进程。这个答案对我有帮助。希望我能多次投票 (2认同)
  • 如果在cmd中使用了分号,则此方法不起作用 (2认同)

Jov*_*vik 46

如果你可以使用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)
Run Code Online (Sandbox Code Playgroud)

  • `AttributeError:'Process'对象没有'pip install psutil`的属性'get_children`. (3认同)
  • 我认为get_children()应该是children().但它在Windows上对我不起作用,这个过程仍然存在. (3认同)
  • @Godsmith - psutil API已经改变,你是对的:[children()](http://pythonhosted.org/psutil/_modules/psutil.html#Process.children)和get_children()一样.如果它在Windows上不起作用,那么您可能想在[GitHub]中创建一个错误票证(https://github.com/giampaolo/psutil) (3认同)

小智 23

我可以用它做

from subprocess import Popen

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

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

(在Windows上)


Sai*_*kat 9

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


epi*_*nal 7

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

以下subprocess.Popen 文件:

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

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()
Run Code Online (Sandbox Code Playgroud)

在我的情况下,我错过了proc.communicate()电话后proc.kill().这会清除进程stdin,stdout ......并终止进程.


Mat*_*ein 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)
Run Code Online (Sandbox Code Playgroud)

那么p.kill()和p.terminate()应该如你所愿.

  • 使用绝对路径而不是更改目录...可选择os.chdir(...)到该目录... (4认同)