2011-01-25 2 views
207

Я запускает подпроцесс с помощью следующей команды:Как прекратить питона подпроцесс запускается с оболочкой = True

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

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

p.terminate() 

или

p.kill() 

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

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

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

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

+0

Как выглядит ваш 'cmd'? Он может содержать команду, которая запускает несколько процессов для запуска. Поэтому неясно, о каком процессе вы говорите. –

+2

Связанный: [Python: как убить дочерний процесс (ы), когда родитель умирает?] (Http://stackoverflow.com/q/23434842/4279) – jfs

ответ

287

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

Вот код:

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 
+1

Является ли 'signal.SIGUSR1' опечаткой? – Tshepang

+9

'preexec_fn = os.setpgrp' также работает –

+20

Работает ли он на Windows? –

5

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

4

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

+1

В моем случае это действительно не помогает, учитывая, что cmd является «cd путь && zsync и т. д. ». Так что на самом деле команда не работает! – user175259

+4

Используйте абсолютные пути вместо изменения каталогов ... Опционально os.chdir (...) в этот каталог ... –

+0

Встроенная функция изменения рабочего каталога для дочернего процесса. Просто передайте аргумент 'cwd' в [' Popen'] (https://docs.python.org/2/library/subprocess.html#subprocess.Popen). –

66
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() должно работать.

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

+0

Это работает на Windows и MAC. – speedyrazor

+1

Хорошее и легкое решение для * nix, спасибо! Работы на Linux, также должны работать и для Mac. – MarSoft

+0

Очень приятное решение. Если ваш cmd оказывается оболочкой сценария оболочки для чего-то другого, вызовите окончательный двоичный код там с помощью exec, чтобы иметь только один подпроцесс. – Nicolinux

16

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

from subprocess import Popen 

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

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

(на Windows)

+2

Это работает для окон. Благодаря! –

30

Если вы можете использовать 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) 
+2

Это работало для меня в Windows. – benjammin

+3

'Объект AttributeError: 'Process' не имеет атрибута 'get_children' для' pip install psutil'. – d33tah

+3

Я думаю, get_children() должен быть child(). Но в Windows это не сработало, процесс все еще существует. – Godsmith

0

Ни один из этих ответов не работал для меня, поэтому Im оставив код, который сделал работу. В моем случае даже после убийства процесса с .kill() и получения кода возврата .poll() процесс не завершился.

После subprocess.Popendocumentation:

«... в целях очистки должным образом благонравным приложение должно убить дочерний процесс и завершить связь ...»

proc = subprocess.Popen(...) 
try: 
    outs, errs = proc.communicate(timeout=15) 
except TimeoutExpired: 
    proc.kill() 
    outs, errs = proc.communicate() 

В моем случае я отсутствовал proc.communicate() после звонка proc.kill(). Это очищает процесс stdin, stdout ... и завершает процесс.