Python 2.6 på Windows: Sådan opsiges subprocess.Popen med 'shell=True' argument?

Indlæg af Hanne Mølgaard Plasc

Problem



Er der en måde at opsige en proces, der er startet med underprocessen. Åbn klasse med 'shell' -argumentet sat til 'True'? I arbejdet minimalt eksempel nedenfor (bruger wxPython) kan du åbne og afslutte en Notesblok proces lykkeligt. Men hvis du ændrer Popen 'shell' -argumentet til 'True', slutter Notepad-processen ikke.


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


Godkend for dette spørgsmål, at 'shell' skal svare til 'True'.

Bedste reference


Hvorfor bruger du shell=True?


Bare gør det. Du behøver det ikke, det påberåber skallen, og det er ubrugeligt.


Jeg accepterer ikke at det skal være True, fordi det ikke gør det. Brug af shell=True bringer kun problemer og ingen fordel. Bare undgå det for enhver pris. Medmindre du kører nogle shell interne kommandoer, behøver du ikke det, nogensinde .

Andre referencer 1


Når du bruger shell=True og kalder afslutte på processen, dræber du faktisk skallen, ikke notisblokken. Skallen ville være det, der er angivet i COMSPEC-miljøvariablen.


Den eneste måde jeg kan tænke på at dræbe denne notesblokproces ville være at bruge Win32process.EnumProcesses () for at søge efter processen, så dræb den ved hjælp af win32api.TerminateProcess. Du vil dog ikke være i stand til at skelne notepad processen fra andre processer med samme navn.

Andre referencer 2


Baseret på det tip, der er givet i Thomas Watnedals svar, hvor han påpeger, at bare skallen faktisk bliver dræbt i eksemplet, har jeg arrangeret følgende funktion, som løser problemet for mit scenario, baseret på eksemplet givet i Mark Hammond s PyWin32 bibliotek:


procname er navnet på processen som set i Task Manager uden udvidelsen, f.eks. FFMPEG.EXE ville være killProcName ('FFMPEG'). Bemærk, at funktionen er rimelig langsom, da den udfører opregning af alle aktuelle køringsprocesser, så resultatet ikke er øjeblikkeligt.


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)

Andre referencer 3


Hvis du virkelig har brug for shell=True flag, så er løsningen at bruge kommandoen start shell med /WAIT flag. Med dette flag vil processen start vente på, at barnet skal ophøre. Ved at bruge for eksempel psutil modulet kan du opnå det ønskede med følgende sekvens: [17]


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


Efter den tredje linje vises Notesblok. poll vender tilbage None så længe vinduet er åbent takket være /WAIT flag. Efter at have dræbt start er barnets notesblokvindue forsvundet, og poll returnerer exitkoden.

Andre referencer 4


Python 2.6 har en kill metode til subprocess.Popen objekter.


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