Python 2.6 on Windows: how to terminate subprocess.Popen with “shell=True” argument?

徘徊边缘 提交于 2019-12-18 06:08:31

问题


Is there a way to terminate a process started with the subprocess.Popen class with the "shell" argument set to "True"? In the working minimal example below (uses wxPython) you can open and terminate a Notepad process happily, however if you change the Popen "shell" argument to "True" then the Notepad process doesn't terminate.

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()

Please accept for the sake of this question that "shell" does have to equal "True".


回答1:


Why are you using shell=True?

Just don't do it. You don't need it, it invokes the shell and that is useless.

I don't accept it has to be True, because it doesn't. Using shell=True only brings you problems and no benefit. Just avoid it at all costs. Unless you're running some shell internal command, you don't need it, ever.




回答2:


When using shell=True and calling terminate on the process, you are actually killing the shell, not the notepad process. The shell would be whatever is specified in the COMSPEC environment variable.

The only way I can think of killing this notepad process would be to use Win32process.EnumProcesses() to search for the process, then kill it using win32api.TerminateProcess. You will however not be able to distinguish the notepad process from other processes with the same name.




回答3:


Based on the tip given in Thomas Watnedal's answer, where he points out that just the shell is actually being killed in the example, I have arranged the following function which solves the problem for my scenario, based on the example given in Mark Hammond's PyWin32 library:

procname is the name of the process as seen in Task Manager without the extension, e.g. FFMPEG.EXE would be killProcName("FFMPEG"). Note that the function is reasonably slow as it performs enumeration of all current running processes so the result is not instant.

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)



回答4:


If you really need the shell=True flag, then the solution is to use the start shell command with the /WAIT flag. With this flag, the start process will wait for its child to terminate. Then, using for example the psutil module you can achieve what you want with the following sequence:

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

After the third line Notepad appears. poll returns None as long as the window is open thanks to the /WAIT flag. After killing start's child Notepad window disappears, and poll returns the exit code.




回答5:


Python 2.6 has a kill method for subprocess.Popen objects.

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



来源:https://stackoverflow.com/questions/696345/python-2-6-on-windows-how-to-terminate-subprocess-popen-with-shell-true-argum

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!