Как завершить подпроцесс Python, запущенный с помощью оболочки = True
-
24-10-2019 - |
Вопрос
Я запускаю подпроцесс с помощью следующей команды:
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...')
Я считаю, что этот метод также кроссплатформенный.