2009-03-30 13 views
7

Esiste un modo per terminare un processo avviato con la sottoprocesso. Classe di classe con l'argomento "shell" impostato su "True"? Nell'esempio di lavoro minimo qui di seguito (utilizza wxPython) è possibile aprire e chiudere un processo di Blocco note in modo soddisfacente, tuttavia se si modifica l'argomento "shell" di Popen su "True", il processo di Blocco note non viene terminato.Python 2.6 su Windows: come terminare subprocess.Popen con argomento "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() 

Si prega di accettare per il gusto di questa domanda che "shell" deve essere uguale a "True".

+0

Quale versione di Python? –

+1

related: [Come terminare un sottoprocesso python lanciato con shell = True] (http: // stackoverflow.it/questions/4789837/how-to-terminate-a-python-subprocess-launch-with-shell-true) – jfs

risposta

2

Sulla base del suggerimento dato nella risposta di Thomas Watnedal, dove sottolinea che solo la shell viene effettivamente uccisa nell'esempio, ho organizzato la seguente funzione che risolve il problema per il mio scenario, in base all'esempio dato in Libreria PyWin32 di Mark Hammond:

procname è il nome del processo come visualizzato in Task Manager senza estensione, ad esempio FFMPEG.EXE sarebbe killProcName ("FFMPEG"). Si noti che la funzione è ragionevolmente lenta poiché esegue l'enumerazione di tutti i processi correnti in esecuzione in modo che il risultato non sia istantaneo.

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) 
+1

-1: la solutuione uccide altri processi che sono in esecuzione con lo stesso nome. – nosklo

+0

Come ho detto, la soluzione funziona per il mio particolare scenario, nel qual caso ci sarà sempre solo un'istanza in esecuzione del processo che devo uccidere. –

+0

Anche il commento nel codice che diceva che ogni istanza è stata uccisa era errata e l'ho aggiornata. –

5

Quando si utilizza shell = True e la chiamata termina nel processo, si sta effettivamente uccidendo la shell, non il processo del blocco note. La shell dovrebbe essere qualsiasi cosa specificata nella variabile di ambiente COMSPEC.

L'unico modo in cui posso pensare di uccidere questo processo di blocco note consiste nell'utilizzare Win32process.EnumProcesses() per cercare il processo, quindi ucciderlo utilizzando win32api.TerminateProcess. Tuttavia, non sarà possibile distinguere il processo del blocco note da altri processi con lo stesso nome.

-1

Python 2.6 ha un metodo kill per gli oggetti subprocess.Popen.

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

+0

La documentazione dice che kill è un alias per terminare sulla piattaforma Windows. Questo è ciò che sta usando nell'esempio che non funziona. –

+2

-1: il metodo kill uccide la shell, non una soluzione – nosklo

+0

@nosklo: hai provato questo? Non posso testarlo; Non ho installato 2.6 su Windows. –

8

Perché stai usando shell=True?

Proprio non farlo. Non ne hai bisogno, invoca il guscio e questo è inutile.

Non accetto che sia True, perché non è così. L'utilizzo di shell=True ti porta solo problemi e nessun vantaggio. Basta evitarlo a tutti i costi. A meno che tu non stia eseguendo alcuni comandi interni della shell, non ne hai bisogno, mai.

0

Se si ha realmente bisogno la bandiera shell=True, allora la soluzione è quella di utilizzare il comando start shell con la bandiera /WAIT. Con questo flag, il processo start attenderà che il figlio si chiuda. Poi, usando per esempio il modulo psutil è possibile ottenere ciò che si vuole con la seguente sequenza:

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

Dopo la terza linea appare Blocco note. poll restituisce None finché la finestra è aperta grazie al flag /WAIT. Dopo aver ucciso la finestra Blocco note figlio di start scompare e poll restituisce il codice di uscita.

Problemi correlati