Как завершить подпроцесс Python, запущенный с помощью оболочки = True

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

Вопрос

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

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

Однако, когда я пытаюсь убить, используя:

p.terminate()

или

p.kill()

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

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

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

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

Это было полезно?

Решение

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

Вот код:

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

Другие советы

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

p.kill() в конечном итоге убивает процесс оболочки и cmd все еще работает.

Я нашел удобное исправление:

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

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

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

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

Если вы можете использовать псаутил, тогда это работает отлично:

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)

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

from subprocess import Popen

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

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

(В окнах)

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

Ни один из этих ответов не сработал для меня, поэтому я покидаю код, который работал. В моем случае даже после убийства процесса .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 ... и завершает процесс.

Как сказал Саи, оболочка - это ребенок, поэтому его перехватывают сигналы - лучший способ, который я обнаружил, - использовать 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 () должны работать так, как вы ожидаете.

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

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

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

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

/Im - это имя изображения, вы также можете сделать /pid, если хотите. /T убивает процесс, а также дочерние процессы. /F сила завершает это. SI, как я устроен, это то, как вы это делаете, не показывая окно CMD. Этот код используется в Python 3.

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

    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)

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

import subprocess

with open(filename,'rb') as f:
    img=f.read()
with subprocess.Popen("/usr/bin/lpr", stdin=subprocess.PIPE) as lpr:
    lpr.stdin.write(img)
print('Printed image...')

Я считаю, что этот метод также кроссплатформенный.

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