2009-03-30 10 views
7

¿Hay alguna manera de finalizar un proceso iniciado con el subproceso. ¿Clase Popen con el argumento "shell" establecido en "True"? En el ejemplo mínimo de trabajo a continuación (utiliza wxPython) puede abrir y finalizar un proceso de Bloc de notas felizmente; sin embargo, si cambia el argumento "shell" de Popen a "True", el proceso de Bloc de notas no finaliza.Python 2.6 en Windows: cómo terminar subproceso.Popen con argumento "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() 

Acepte, por el bien de esta pregunta, que "shell" tiene que ser igual a "Verdadero".

+0

¿Qué versión de Python? –

+1

relacionado: [Cómo finalizar un subproceso de python iniciado con shell = True] (http: // stackoverflow.com/questions/4789837/how-to-terminate-a-python-subprocess-launch-with-shell-true) – jfs

Respuesta

2

Basado en el consejo dado en la respuesta de Thomas Watnedal, donde señala que solo el caparazón se está matando en el ejemplo, he organizado la siguiente función que resuelve el problema para mi escenario, basado en el ejemplo dado en Biblioteca PyWin32 de Mark Hammond:

procname es el nombre del proceso como se ve en el Administrador de tareas sin la extensión, por ejemplo FFMPEG.EXE sería killProcName ("FFMPEG"). Tenga en cuenta que la función es razonablemente lenta ya que realiza la enumeración de todos los procesos en ejecución actuales, por lo que el resultado no es instantáneo.

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: solutuion elimina otros procesos que se están ejecutando con el mismo nombre. – nosklo

+0

Como dije, la solución funciona para MI escenario particular en cuyo caso siempre habrá solo una instancia en ejecución del proceso que necesito matar. –

+0

También el comentario en el código que decía que se mata cada instancia era incorrecto y lo he actualizado. –

5

Cuando se utiliza shell = True y la llamada termina en el proceso, en realidad se está eliminando el shell, no el proceso de la libreta. El shell sería lo que se especifique en la variable de entorno COMSPEC.

La única forma en que puedo pensar en matar este proceso de bloc de notas sería usar Win32process.EnumProcesses() para buscar el proceso, luego matarlo usando win32api.TerminateProcess. Sin embargo, no podrá distinguir el proceso del bloc de notas de otros procesos con el mismo nombre.

-1

Python 2.6 tiene un método kill para el subproceso. Objetos abiertos.

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

+0

La documentación dice que kill es un alias para terminar en la plataforma de Windows. Eso es lo que está usando en el ejemplo que no funciona. –

+2

-1: el método kill matará el shell, no una solución – nosklo

+0

@nosklo: ¿has probado esto? No puedo probarlo; No tengo 2.6 instalado en Windows. –

8

¿Por qué utiliza shell=True?

Simplemente no lo hagas. No lo necesita, invoca el caparazón y eso es inútil.

No lo acepto, tiene que ser True, porque no es así. Usar shell=True solo le trae problemas y ningún beneficio. Solo evítalo a toda costa. A menos que esté ejecutando algún comando interno del shell, no lo necesita, alguna vez.

0

Si realmente necesita la bandera shell=True, entonces la solución es utilizar el comando shell start con la bandera /WAIT. Con este indicador, el proceso start esperará a que su hijo finalice. Luego, utilizando por ejemplo el módulo psutil se puede lograr lo que quiere con la siguiente secuencia:

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

Después de la tercera línea aparece el Bloc de notas. poll devuelve None mientras la ventana esté abierta gracias al indicador /WAIT. Después de matar start, la ventana del Bloc de notas secundario desaparece y poll devuelve el código de salida.

Cuestiones relacionadas