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