Python 2.6 no Windows: como encerrar subprocess.Popen com “shell = True” argumento?
-
22-08-2019 - |
Pergunta
Existe uma maneira de encerrar um processo iniciado com a classe subprocess.Popen com o "shell" set argumento para "True"? No exemplo mínima trabalhando abaixo (usa wxPython) você pode abrir e encerrar um processo Notepad feliz, no entanto, se você alterar o argumento Popen "shell" para "True", em seguida, o processo de bloco de notas não terminar.
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()
Por favor, aceite por causa desta questão que "shell" tem que ser igual "True".
Solução 3
Com base na dica dada resposta de Thomas Watnedal, onde ele aponta que apenas o shell está realmente sendo mortos no exemplo, eu arranjei a função após o que resolve o problema para o meu cenário, com base no exemplo dado em Marcos biblioteca PyWin32 de Hammond:
procname é o nome do processo, como visto no Gerenciador de tarefas sem a extensão, por exemplo, FFMPEG.EXE seria killProcName ( "FFMPEG"). Note-se que a função é razoavelmente lento como ele executa a enumeração de todos os processos em execução atuais de modo que o resultado não é imediato.
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)
Outras dicas
Por que você está usando shell=True
?
Apenas não fazê-lo. Você não precisa dele, ele chama o shell e que é inútil.
Eu não aceito que tem que ser True
, porque isso não acontece. Usando shell=True
só traz-lhe problemas e nenhum benefício. Apenas evitá-lo a todo custo. A menos que você estiver executando algum comando interno de shell, você não precisa dele, sempre .
Ao usar shell = true e chamando terminará no processo, você está realmente matando o shell, e não o processo de bloco de notas. O escudo seria tudo o que está especificado na variável de ambiente COMSPEC.
A única maneira que eu posso pensar de matar esse processo notepad seria usar Win32process.EnumProcesses () para procurar o processo, e depois matá-lo usando win32api.TerminateProcess. Você no entanto, não será capaz de distinguir o processo de bloco de notas de outros processos com o mesmo nome.
Se você realmente precisa a bandeira shell=True
, então a solução é usar o comando shell start
com a bandeira /WAIT
. Com este sinalizador, o processo start
vai esperar por seu filho terminar. Em seguida, usando por exemplo, o psutil
módulo que você pode conseguir o que você quer com a sequência seguinte:
>>> 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
>>>
Depois da terceira linha Notepad aparece. poll
retornos None
enquanto a janela estiver aberta graças à bandeira /WAIT
. Depois de matar desaparece janela Notepad filho de start
e poll
retorna o código de saída.
Python 2.6 tem um método kill objetos subprocess.Popen.
http://docs.python.org/library/subprocess .html # subprocess.Popen.kill