it-swarm.com.ru

Как завершить подпроцесс python, запущенный с Shell = True

Я запускаю подпроцесс с помощью следующей команды:

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)

Тем не менее, когда я пытаюсь убить с помощью:

p.terminate()

или же 

p.kill()

Команда продолжает работать в фоновом режиме, поэтому мне было интересно, как я могу на самом деле завершить процесс. 

Обратите внимание, что когда я запускаю команду с:

p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

Он успешно завершается при выдаче p.terminate().

250
user175259

Используйте группу процессов , чтобы разрешить отправку сигнала всем процессам в группах. Для этого вы должны прикрепить идентификатор сессии к родительскому процессу порожденных/дочерних процессов, который в вашем случае является Shell. Это сделает его лидером группы процессов. Поэтому теперь, когда сигнал отправляется руководителю группы процессов, он передается всем дочерним процессам этой группы.

Вот код:

import os
import signal
import subprocess

# The os.setsid() is passed in the argument preexec_fn so
# it's run after the fork() and before  exec() to run the Shell.
pro = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                       Shell=True, preexec_fn=os.setsid) 

os.killpg(os.getpgid(pro.pid), signal.SIGTERM)  # Send the signal to all the process groups
349
mouad
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)
p.kill()

p.kill() завершает работу процесса Shell, а cmd все еще выполняется.

Я нашел удобное решение этой проблемы:

p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, Shell=True)

Это заставит cmd наследовать процесс Shell вместо того, чтобы Shell запускала дочерний процесс, который не был уничтожен. Тогда p.pid будет идентификатором вашего процесса cmd.

p.kill() должен работать.

Я не знаю, как это повлияет на твою трубу.

80
Bryant Hansen

Если вы можете использовать psutil , то это прекрасно работает:

import subprocess

import psutil


def kill(proc_pid):
    process = psutil.Process(proc_pid)
    for proc in process.children(recursive=True):
        proc.kill()
    process.kill()


proc = subprocess.Popen(["infinite_app", "param"], Shell=True)
try:
    proc.wait(timeout=3)
except subprocess.TimeoutExpired:
    kill(proc.pid)
39
Jovik

Я мог бы сделать это с помощью 

from subprocess import Popen

process = Popen(command, Shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))

это убило cmd.exe и программу, для которой я дал команду.

(В Windows)

19
SPratap

Когда Shell=True, Shell является дочерним процессом, а команды - его дочерними. Таким образом, любой SIGTERM или SIGKILL убьет Shell, но не его дочерние процессы, и я не помню хорошего способа сделать это . Лучший способ, о котором я могу подумать, - это использовать Shell=False, иначе, когда вы убьете родительский процесс Shell, он оставит несуществующий процесс Shell.

8
Sai Venkat

Ни один из этих ответов не работал для меня, поэтому я оставляю код, который работал. В моем случае даже после завершения процесса с помощью .kill() и получения кода возврата .poll() процесс не завершился. 

Следуя subprocess.Popenдокументации :

«... для правильной очистки приложение с хорошим поведением должно завершить дочерний процесс и завершить связь ...»

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

В моем случае я пропустил proc.communicate() после вызова proc.kill(). Это очищает процесс stdin, stdout ... и завершает процесс.

6
epinal

Как сказал Сай, Shell является дочерней, поэтому она перехватывает сигналы - лучший способ, который я нашел, это использовать Shell = False и использовать shlex для разделения командной строки:

if isinstance(command, unicode):
    cmd = command.encode('utf8')
args = shlex.split(cmd)

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Тогда p.kill () и p.terminate () должны работать так, как вы ожидаете.

5
Matt Billenstein

что я чувствую, что мы могли бы использовать:

import os
import signal
import subprocess
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)

os.killpg(os.getpgid(pro.pid), signal.SIGINT)

это не убьет всю вашу задачу, но процесс с p.pid

1
Nilesh K.

Я знаю, что это старый вопрос, но это может помочь кому-то искать другой метод. Это то, что я использую в Windows, чтобы убить процессы, которые я вызвал. 

si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)

/ IM - это имя изображения, вы также можете сделать/PID, если хотите./T убивает процесс, а также дочерние процессы./F сила прекращает его. Си, как я установил, это то, как вы делаете это, не показывая окно CMD. Этот код используется в Python 3.

0
Zx4161

Отправить сигнал всем процессам в группе

    self.proc = Popen(commands, 
            stdout=PIPE, 
            stderr=STDOUT, 
            universal_newlines=True, 
            preexec_fn=os.setsid)

    os.killpg(os.getpgid(self.proc.pid), signal.SIGHUP)
    os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM)
0
roger.wang