Как завершить подпроцесс python, запущенный с помощью оболочки = True
Я запускаю подпроцесс со следующей командой:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
Однако, когда я пытаюсь убить, используя:
p.terminate()
или
p.kill()
Команда продолжает работать в фоновом режиме, поэтому мне было интересно, как я могу закончить процесс.
Обратите внимание, что когда я запускаю команду с помощью:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
Он успешно завершается при выпуске p.terminate()
.
Ответы
Ответ 1
Используйте группу процессов чтобы включить отправку сигнала ко всему процессу в группах. Для этого вы должны прикрепить идентификатор сеанса к родительскому процессу порожденных/дочерних процессов, который является оболочкой в вашем случае. Это сделает его лидером группы процессов. Итак, теперь, когда сигнал отправляется лидеру группы процессов, он передается всем дочерним процессам этой группы.
Здесь код:
import os
import signal
import subprocess
# The os.setsid() is passed in the argument preexec_fn so
# it 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
Ответ 2
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()
должен работать.
Я не знаю, какой эффект это будет иметь на вашей трубе.
Ответ 3
Если вы можете использовать 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)
Ответ 4
Я мог бы сделать это, используя
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
он убил cmd.exe
и программу, для которой я дал команду.
(В Windows)
Ответ 5
Когда shell=True
оболочка является дочерним процессом, а командами являются ее дочерние элементы. Таким образом, любые SIGTERM
или SIGKILL
будут убивать оболочку, но не ее дочерние процессы, и я не помню хороший способ сделать это.
Лучший способ, который я могу придумать, - использовать shell=False
, иначе, когда вы убьете процесс родительской оболочки, он оставит нерабочий процесс оболочки.
Ответ 6
Ни один из этих ответов не работал для меня, поэтому я оставляю код, который работал. В моем случае даже после завершения процесса с помощью .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... и завершает процесс.
Ответ 7
Как сказал Саи, оболочка - это ребенок, поэтому сигналы перехватываются им - лучший способ, который я нашел, - использовать 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() должны работать так, как вы ожидаете.
Ответ 8
что я чувствую, что мы могли бы использовать:
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
Ответ 9
Я знаю, что это старый вопрос, но это может помочь кому-то искать другой метод. Это то, что я использую в 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.
Ответ 10
Отправить сигнал всем процессам в группе
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)
Ответ 11
Я не видел здесь упомянутое, поэтому выкладываю на всякий случай, если кому-то это нужно. Если все, что вы хотите сделать, это убедиться, что ваш подпроцесс завершается успешно, вы можете поместить его в контекстный менеджер. Например, я хотел, чтобы мой стандартный принтер распечатывал изображение, и с помощью диспетчера контекста гарантировал, что подпроцесс завершился.
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...')
Я считаю, что этот метод также кроссплатформенный.