Python 2.6 в Windows:как завершить subprocess.Popen с аргументом «shell = True»?

StackOverflow https://stackoverflow.com/questions/696345

Вопрос

Есть ли способ завершить процесс, запущенный с помощью класса 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top