Python 2.6 в Windows: как завершить подпроцесс. Открыть с аргументом «shell = True»? - PullRequest
7 голосов
/ 30 марта 2009

Есть ли способ завершить процесс, запущенный с помощью класса subprocess.Popen с аргументом "shell", установленным в "True"? В приведенном ниже рабочем минимальном примере (использует wxPython) вы можете счастливо открыть и завершить процесс Notepad, однако если вы измените аргумент Popen «shell» на «True», то процесс Notepad не завершится.

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

Пожалуйста, примите во внимание, что «оболочка» должна быть равна «Истина».

Ответы [ 5 ]

8 голосов
/ 30 марта 2009

Почему вы используете shell=True?

Только не делай этого. Вам это не нужно, оно вызывает оболочку, и это бесполезно.

Я не принимаю, это должно быть True, потому что это не так. Использование shell=True только приносит вам проблемы и не приносит никакой пользы. Просто избегайте этого любой ценой. Если вы не используете какую-либо внутреннюю команду оболочки, она вам не нужна, ever .

5 голосов
/ 30 марта 2009

Когда используется shell = True и вызов завершается в процессе, вы на самом деле убиваете оболочку, а не процесс блокнота. Оболочкой будет все, что указано в переменной среды COMSPEC.

Единственный способ, которым я могу думать об уничтожении этого процесса блокнота, состоит в том, чтобы использовать Win32process.EnumProcesses () для поиска процесса, а затем уничтожить его с помощью win32api.TerminateProcess. Однако вы не сможете отличить процесс блокнота от других процессов с таким же именем.

2 голосов
/ 30 марта 2009

Основываясь на подсказке, приведенной в ответе Томаса Ватнедала, где он указывает, что в этом примере фактически убивается только оболочка, я организовал следующую функцию, которая решает проблему для моего сценария, на основе примера, приведенного в Марке. Библиотека Хаммонда 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)
0 голосов
/ 29 декабря 2013

Если вам действительно нужен флаг 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 возвращает код выхода.

0 голосов
/ 30 марта 2009

В Python 2.6 есть метод kill для подпроцесса. Открытые объекты.

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

...