Python 2.6 в Windows:как завершить subprocess.Popen с аргументом «shell = True»?
-
22-08-2019 - |
Вопрос
Есть ли способ завершить процесс, запущенный с помощью класса subprocess.Popen с аргументом «shell», установленным в «True»?В приведенном ниже рабочем минимальном примере (используется wxPython) вы можете успешно открывать и завершать процесс «Блокнота», однако, если вы измените аргумент «оболочки» Popen на «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()
Пожалуйста, примите во внимание этот вопрос, что «оболочка» действительно должна равняться «Истина».
Решение 3
Основываясь на совете, данном в ответе Томаса Уотнедала, где он указывает, что в этом примере на самом деле уничтожается только оболочка, я организовал следующую функцию, которая решает проблему для моего сценария на основе примера, приведенного в библиотеке PyWin32 Марка Хаммонда. :
procname — это имя процесса, отображаемое в диспетчере задач, без расширения, например.FFMPEG.EXE будет иметь имя killProcName("FFMPEG").Обратите внимание, что функция работает достаточно медленно, поскольку она перечисляет все текущие запущенные процессы, поэтому результат не является мгновенным.
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)
Другие советы
Почему вы используете shell=True
?
Просто не делай этого.Вам это не нужно, он вызывает оболочку, а это бесполезно.
Я не согласен, что это должно быть True
, потому что это не так.С использованием shell=True
приносит вам только проблемы и никакой пользы.Просто избегайте этого любой ценой.Если вы не запускаете какую-либо внутреннюю команду оболочки, она вам не нужна, всегда.
При использовании Shell=True и вызове завершения процесса вы фактически убиваете оболочку, а не процесс блокнота.Оболочкой будет то, что указано в переменной среды COMSPEC.
Единственный способ убить этот процесс блокнота — использовать Win32process.EnumProcesses() для поиска процесса, а затем убить его с помощью win32api.TerminateProcess.Однако вы не сможете отличить процесс «Блокнот» от других процессов с таким же именем.
Если вам действительно нужно shell=True
флаг, то решение состоит в том, чтобы использовать флаг start
команда оболочки с помощью /WAIT
флаг.С этим флагом start
процесс будет ждать завершения своего дочернего процесса.Затем, используя, например, psutil
модуль, вы можете добиться того, чего хотите, с помощью следующей последовательности:
>>> 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
>>>
После третьей строки появляется Блокнот. poll
возвращает None
пока окно открыто благодаря /WAIT
флаг.После убийства start
дочернее окно Блокнота исчезает, и poll
возвращает код выхода.
В Python 2.6 есть метод уничтожения объектов subprocess.Popen.
http://docs.python.org/library/subprocess.html#subprocess.Popen.kill