Как завершить подпроцесс 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()
.
7 ответов
использовать групповой процесс чтобы включить отправку сигнала для всего процесса в группах. Для этого вы должны приложить идентификатор сессии к родительскому процессу порожденных / дочерних процессов, который является оболочкой в вашем случае. Это сделает его лидером группы процессов. Теперь, когда сигнал передается руководителю группы процессов, он передается всем дочерним процессам этой группы.
вот код:
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()
убивает процесс Shell и cmd
по-прежнему работает.
Я нашел удобное исправление этого:
p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
это заставит cmd наследовать процесс оболочки, вместо того, чтобы запускать дочерний процесс оболочки, который не будет убит. p.pid
будет идентификатором вашего процесса cmd.
p.kill()
должны работать.
Я не знаю, какой эффект это будет иметь на вашей трубе, хотя.
Если вы можете использовать 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)
я мог бы сделать это, используя
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
Он убил cmd.exe
и программа, для которой я дал команду.
(В Windows)
, когда 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()
. Этот чистый поток ввода процесса, поток 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 () должен работать так, как вы ожидаете.