Windows上的Python 2.6:如何使用“shell = True”参数终止subprocess.Popen?

时间:2009-03-30 07:56:28

标签: python windows windows-xp subprocess python-2.6

有没有办法终止使用subprocess.Popen类启动的进程,并将“shell”参数设置为“True”?在下面的工作最小示例(使用wxPython)中,您可以愉快地打开和终止记事本进程,但是如果将Popen“shell”参数更改为“True”,则记事本进程不会终止。

import wx
import threading
import subprocess

class MainWindow(wx.Frame):

    def __init__(self, parent, id, title):        
        wx.Frame.__init__(self, parent, id, title)
        self.main_panel = wx.Panel(self, -1)

        self.border_sizer = wx.BoxSizer()

        self.process_button = wx.Button(self.main_panel, -1, "Start process", (50, 50))
        self.process_button.Bind(wx.EVT_BUTTON, self.processButtonClick)

        self.border_sizer.Add(self.process_button)
        self.main_panel.SetSizerAndFit(self.border_sizer)
        self.Fit()

        self.Centre()
        self.Show(True)

    def processButtonClick(self, event):
        if self.process_button.GetLabel() == "Start process":
            self.process_button.SetLabel("End process")
            self.notepad = threading.Thread(target = self.runProcess)
            self.notepad.start()
        else:
            self.cancel = 1
            self.process_button.SetLabel("Start process")

    def runProcess(self):
        self.cancel = 0

        notepad_process = subprocess.Popen("notepad", shell = False)

        while notepad_process.poll() == None: # While process has not yet terminated.
            if self.cancel:
                notepad_process.terminate()
                break

def main():
    app = wx.PySimpleApp()
    mainView = MainWindow(None, wx.ID_ANY, "test")
    app.MainLoop()

if __name__ == "__main__":
    main()

为了这个问题,请接受“shell”必须等于“True”。

5 个答案:

答案 0 :(得分:8)

您为什么使用shell=True

就是不要这样做。你不需要它,它会调用shell而且没用。

我不接受它必须是True,因为它不是。使用shell=True只会给您带来问题并且没有任何好处。不惜一切代价避免它。除非你正在运行一些shell内部命令,否则你不需要它,永远

答案 1 :(得分:5)

当使用shell = True并在进程上调用terminate时,实际上是在杀死shell而不是记事本进程。 shell将是COMSPEC环境变量中指定的任何内容。

我能想到杀死这个记事本进程的唯一方法是使用Win32process.EnumProcesses()来搜索进程,然后使用win32api.TerminateProcess将其终止。但是,您无法区分记事本流程与其他具有相同名称的流程。

答案 2 :(得分:2)

根据Thomas Watnedal的回答中给出的提示,他指出实际上只是shell被杀死了,我已经安排了以下函数来解决我的场景中的问题,基于Mark给出的示例Hammond的PyWin32库:

procname是在没有扩展名的任务管理器中看到的进程的名称,例如FFMPEG.EXE将是killProcName(“FFMPEG”)。请注意,该函数运行速度相当慢,因为它执行所有当前正在运行的进程的枚举,因此结果不是即时的。

import win32api
import win32pdhutil
import win32con

def killProcName(procname):
    """Kill a running process by name.  Kills first process with the given name."""
    try:
        win32pdhutil.GetPerformanceAttributes("Process", "ID Process", procname)
    except:
        pass

    pids = win32pdhutil.FindPerformanceAttributesByName(procname)

    # If _my_ pid in there, remove it!
    try:
        pids.remove(win32api.GetCurrentProcessId())
    except ValueError:
        pass

    handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pids[0])
    win32api.TerminateProcess(handle, 0)
    win32api.CloseHandle(handle)

答案 3 :(得分:0)

如果您确实需要shell=True标志,那么解决方案是使用带有start标志的/WAIT shell命令。使用此标志,start进程将等待其子进程终止。然后,使用例如psutil模块,您可以按以下顺序实现所需:

>>> import psutil
>>> import subprocess
>>> doc = subprocess.Popen(["start", "/WAIT", "notepad"], shell=True)
>>> doc.poll()
>>> psutil.Process(doc.pid).get_children()[0].kill()
>>> doc.poll()
0
>>> 

第三行记事本出现后。只要窗口打开,poll就会返回None,这要归功于/WAIT标记。杀死start之后,子记事本窗口消失,poll返回退出代码。

答案 4 :(得分:-1)

Python 2.6为subprocess.Popen对象提供了kill方法。

http://docs.python.org/library/subprocess.html#subprocess.Popen.kill