shell = True로 시작된 파이썬 서브 프로세스를 종료하는 방법


308

다음 명령으로 하위 프로세스를 시작합니다.

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

그러나 내가 사용하여 죽일 때 :

p.terminate()

또는

p.kill()

명령이 백그라운드에서 계속 실행되므로 실제로 프로세스를 종료하는 방법이 궁금합니다.

내가 명령을 실행할 때 다음에 유의하십시오.

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

을 발행하면 성공적으로 종료됩니다 p.terminate().


당신의 cmd모습은 어떻습니까? 여러 프로세스가 시작되도록 트리거하는 명령이 포함될 수 있습니다. 따라서 어떤 프로세스에 대해 이야기하는지 명확하지 않습니다.
Robert Siemer


1
shell=True큰 차이 가 없습니다 ?
Charlie Parker

답변:


410

그룹의 모든 프로세스에 신호를 보낼 수 있도록 프로세스 그룹을 사용하십시오 . 이를 위해서는 세션 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

2
@PiotrDobrogost : 슬프게도, os.setsidWindows에서는 사용할 수 없기 때문에 ( docs.python.org/library/os.html#os.setsid ), 이것이 도움이되는지 모르겠지만 여기에서 볼 수는 있습니다 ( bugs.python.org / issue5115 ) 수행 방법에 대한 통찰력이 필요합니다.
mouad

6
어떻게 subprocess.CREATE_NEW_PROCESS_GROUP이 관계?
Piotr Dobrogost

11
우리의 테스트는 setsid! = setpgid라고 제안하고 os.pgkill은 여전히 ​​동일한 프로세스 그룹 ID를 가진 하위 프로세스 만 종료합니다. 프로세스 그룹을 변경 한 프로세스는 여전히 동일한 세션 ID를 가질 수 있지만 종료되지 않습니다.
hwjp


4
os.setsid ()는 다른 효과도 있으므로 권장하지 않습니다. 무엇보다도 제어 TTY 연결을 끊고 새 프로세스를 프로세스 그룹 리더로 만듭니다. win.tue.nl/~aeb/linux/lk/lk-10.html
parasietje

92
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 프로세스의 ID가됩니다.

p.kill() 작동해야합니다.

그래도 이것이 파이프에 어떤 영향을 미칠지 모르겠습니다.


1
* nix를위한 멋지고 가벼운 솔루션, 감사합니다! Linux에서도 작동하며 Mac에서도 작동합니다.
MarSoft

아주 좋은 해결책입니다. cmd가 다른 것을위한 쉘 스크립트 래퍼 인 경우 하위 프로세스가 하나만 있도록 exec와 함께 최종 바이너리를 호출하십시오.
Nicolinux

1
이것은 아름답다. 우분투에서 작업 공간 당 하위 프로세스를 생성하고 종료하는 방법을 알아 내려고 노력했습니다. 이 답변이 도움이되었습니다. 내가 것보다 한 번 더 그것을 upvote에 수 소원
세르지 Kolodyazhnyy

2
cmd에서 세미콜론을 사용하면 작동하지 않습니다.
gnr

@speedyrazor-Windows10에서 작동하지 않습니다. os 특정 답변은 명확하게 표시되어야한다고 생각합니다.
DarkLight

48

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)

3
AttributeError: 'Process' object has no attribute 'get_children에 대한 pip install psutil.
d33tah

3
get_children ()은 children ()이어야한다고 생각합니다. 그러나 Windows에서는 저에게 효과가 없었으며 프로세스는 여전히 있습니다.
대장장이

3
@Godsmith-psutil API가 변경되었으며 옳습니다. children () 은 get_children ()과 동일한 기능을 수행합니다. Windows에서 작동하지 않으면 GitHub
Jovik

24

나는 그것을 사용하여 그것을 할 수있다

from subprocess import Popen

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

그것은 cmd.exe내가 명령을 준 프로그램과 프로그램을 죽였습니다 .

(Windows에서)


또는 프로세스 이름 :을 사용 Popen("TASKKILL /F /IM " + process_name)하지 않으면 command매개 변수 에서 가져올 수 있습니다 .
zvi

12

shell=True쉘이 자식 프로세스이며, 명령은 그 아이입니다. 어떤 그래서 SIGTERMSIGKILL자식 프로세스 쉘을 죽일 수 있지만, 나는 그것을 할 수있는 좋은 방법을 기억하지 않습니다. 내가 생각할 수있는 가장 좋은 방법은을 사용하는 것입니다 shell=False. 그렇지 않으면 부모 셸 프로세스를 종료하면 사용되지 않는 셸 프로세스가 남습니다.


8

이 답변 중 어느 것도 나를 위해 일하지 않았으므로 Im은 작동 한 코드를 남겨 둡니다. 필자의 경우 프로세스를 종료 .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 ...을 정리하고 프로세스를 종료합니다.


이 솔루션은 Linux 및 Python 2.7에서 작동하지 않습니다.
user9869932

@xyz Linux 및 python 3.5에서 작동했습니다. 파이썬 2.7의 문서를 확인
에피 날

@espinal, 감사합니다. 아마도 리눅스 문제 일 것입니다. Raspberry 3에서 실행되는 Raspbian Linux입니다.
user9869932

5

Sai가 말했듯이 쉘은 자식이므로 신호가 차단됩니다. 내가 찾은 가장 좋은 방법은 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 ()가 예상대로 작동합니다.


1
제 경우에는 cmd가 "cd path && zsync etc etc"라는 것을 감안할 때 실제로 도움이되지 않습니다. 따라서 실제로 명령이 실패합니다!
user175259

4
... 그 디렉토리에 대한 절대 경로를 변경하는 대신 디렉토리 ... 선택적으로의 os.chdir을 (...)를 사용
매트 Billenstein

하위 프로세스의 작업 디렉토리를 변경하는 기능이 내장되어 있습니다. 에 cwd인수를 전달하십시오 Popen.
Jonathon Reinhart 1

나는 shlex를 사용했지만 여전히 문제가 지속되며 kill은 자식 프로세스를 죽이지 않습니다.
hungryWolf 2012 년

1

내가 사용할 수 있다고 생각하는 것 :

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를 사용한 프로세스


0

나는 이것이 오래된 질문이라는 것을 알고 있지만 다른 방법을 찾는 사람에게 도움이 될 수 있습니다. 이것이 내가 호출 한 프로세스를 죽이기 위해 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 창을 표시하지 않고이 작업을 수행하는 방법입니다. 이 코드는 파이썬 3에서 사용됩니다.


0

그룹의 모든 프로세스에 신호를 보냅니다.

    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)

0

나는 여기에 언급 된 것을 보지 못했기 때문에 누군가가 필요로하는 경우를 대비하여 거기에두고 있습니다. 서브 프로세스가 성공적으로 종료되도록하는 것만으로 컨텍스트 매니저에 넣을 수 있습니다. 예를 들어, 표준 프린터에서 출력 이미지를 인쇄하고 컨텍스트 관리자를 사용하여 하위 프로세스가 종료되도록했습니다.

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...')

이 방법도 플랫폼 간이라고 생각합니다.


여기서 코드가 프로세스를 종료하는 방법을 알 수 없습니다. 당신은 명확히 할 수 있습니까?
DarkLight

나는 당신이하고 싶은 모든 것이 코드의 다음 줄로 진행하기 전에 프로세스가 종료되었는지 확인하면이 방법을 사용할 수 있음을 분명히 밝혔습니다. 나는 그것이 프로세스를 종료한다고 주장하지 않았다.
Jaiyam Sharma

컨텍스트 매니저가 당신이 명확하게 언급 한대로, "당신의 프로세스가 종료되었는지 확인합니다"경우에 당신은 어떻게 이 프로세스를 종료한다는 주장. 그래도? shell=True질문에 따라로 프로세스가 시작된 경우에도 ? 어떤이 아닌 코드에서.
anon

anon, 'terminated'라는 단어의 혼란스러운 사용을 지적 해 주셔서 감사합니다. 컨텍스트 관리자는 서브 라인이 완료 될 때까지 기다렸다가 마지막 행의 print 문으로 이동하십시오. 이것은 sigterm과 같은 것을 사용하여 프로세스를 즉시 종료하는 것과 다릅니다. 스레드와 호출을 사용하여 동일한 결과를 얻을 수 있습니다 thread.join(). 이것이 정리되기를 바랍니다.
Jaiyam Sharma
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.