Cách chấm dứt một quy trình con python được khởi chạy với shell = True


308

Tôi đang khởi chạy một quy trình con với lệnh sau:

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

Tuy nhiên, khi tôi cố gắng giết bằng cách sử dụng:

p.terminate()

hoặc là

p.kill()

Lệnh tiếp tục chạy trong nền, vì vậy tôi đã tự hỏi làm thế nào tôi thực sự có thể chấm dứt quá trình.

Lưu ý rằng khi tôi chạy lệnh với:

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

Nó chấm dứt thành công khi ban hành p.terminate().


Bạn cmdtrông như thế nào? Nó có thể chứa một lệnh kích hoạt một số quy trình được bắt đầu. Vì vậy, không rõ quá trình bạn nói về.
Robert Siemer


1
không có shell=Truesự khác biệt lớn?
Charlie Parker

Câu trả lời:


410

Sử dụng một nhóm quy trình để cho phép gửi tín hiệu đến tất cả quy trình trong các nhóm. Vì vậy, bạn nên đính kèm id phiên vào quy trình cha của các quy trình được sinh ra / con, đây là một vỏ trong trường hợp của bạn. Điều này sẽ làm cho nó trở thành trưởng nhóm của các quy trình. Vì vậy, bây giờ, khi một tín hiệu được gửi đến trưởng nhóm quy trình, nó sẽ được truyền đến tất cả các quy trình con của nhóm này.

Đây là mã:

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: Đáng buồn là không, bởi vì os.setsidkhông có sẵn trong các cửa sổ ( docs.python.org/library/os.html#os.setsid ), tôi không biết nếu điều này có thể giúp nhưng bạn có thể xem tại đây ( bugs.python.org / vấn đề5115 ) cho một số hiểu biết về cách làm điều đó.
mouad

6
Làm thế nào không subprocess.CREATE_NEW_PROCESS_GROUPliên quan đến điều này?
Piotr Dobrogost

11
thử nghiệm của chúng tôi là setid! = setpgid và os.pgkill chỉ giết các quy trình con vẫn có cùng id nhóm quy trình. các quy trình đã thay đổi nhóm quy trình không bị giết, mặc dù chúng vẫn có thể có cùng phiên id ...
hwjp


4
Tôi không khuyên bạn nên làm os.setsid (), vì nó cũng có tác dụng khác. Trong số những người khác, nó ngắt kết nối TTY kiểm soát và làm cho quy trình mới trở thành người lãnh đạo nhóm quy trình. Xem win.tue.nl/~aeb/linux/lk/lk-10.html
parasietje

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

p.kill()kết thúc việc giết quá trình vỏ và cmdvẫn đang chạy.

Tôi tìm thấy một sửa chữa thuận tiện này bằng cách:

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

Điều này sẽ khiến cmd kế thừa tiến trình shell, thay vì shell sẽ khởi chạy tiến trình con, không bị giết. p.pidsẽ là id của quá trình cmd của bạn sau đó.

p.kill() nên làm việc.

Tôi không biết điều này sẽ có ảnh hưởng gì đến đường ống của bạn.


1
Giải pháp tốt đẹp và nhẹ nhàng cho * nix, cảm ơn! Hoạt động trên Linux, cũng nên hoạt động cho Mac.
MarSoft

Giải pháp rất hay. Nếu cmd của bạn tình cờ là một trình bao bọc kịch bản shell cho một cái gì đó khác, hãy gọi nhị phân cuối cùng ở đó với exec cũng để chỉ có một quy trình con.
Nicolinux

1
Thật là đẹp Tôi đã cố gắng tìm ra cách sinh ra và tiêu diệt một quy trình con trên mỗi không gian làm việc trên Ubuntu. Câu trả lời này đã giúp tôi. Ước gì tôi có thể nâng cao nó hơn một lần
Sergiy Kolodyazhnyy

2
điều này không hoạt động nếu một dấu chấm phẩy được sử dụng trong cmd
gnr

@speedyrazor - Không hoạt động trên Windows10. Tôi nghĩ rằng câu trả lời cụ thể os nên được đánh dấu rõ ràng như vậy.
DarkLight

48

Nếu bạn có thể sử dụng psutil , thì nó hoạt động hoàn hảo:

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_childrencho pip install psutil.
d33tah

3
Tôi nghĩ get_children () nên là trẻ em (). Nhưng nó không hoạt động với tôi trên Windows, quá trình vẫn còn đó.
Godsmith

3
@Godsmith - API psutil đã thay đổi và bạn đúng: children () thực hiện điều tương tự như get_children () đã từng. Nếu nó không hoạt động trên Windows, thì bạn có thể muốn tạo một vé lỗi trong GitHub
Jovik

24

Tôi có thể làm điều đó bằng cách sử dụng

from subprocess import Popen

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

nó đã giết cmd.exe và chương trình mà tôi đã ra lệnh cho.

(Trên Windows)


Hoặc sử dụng tên quy trình : Popen("TASKKILL /F /IM " + process_name), nếu bạn không có nó, bạn có thể lấy nó từ commandtham số.
zvi

12

Khi shell=Trueshell là tiến trình con và các lệnh là con của nó. Vì vậy, bất kỳ SIGTERMhoặc SIGKILLsẽ giết chết vỏ nhưng không phải là quá trình con của nó và tôi không nhớ một cách tốt để làm điều đó. Cách tốt nhất tôi có thể nghĩ đến là sử dụng shell=False, nếu không, khi bạn giết tiến trình shell cha, nó sẽ để lại một quy trình shell không còn tồn tại.


8

Không có câu trả lời nào trong số này làm việc cho tôi vì vậy tôi để lại mã đã làm việc. Trong trường hợp của tôi ngay cả sau khi giết quá trình với .kill()và nhận được một.poll() mã trả về, quy trình vẫn không chấm dứt.

Theo subprocess.Popen tài liệu :

"... để dọn dẹp đúng cách, một ứng dụng hoạt động tốt sẽ giết chết quá trình con và kết thúc giao tiếp ..."

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

Trong trường hợp của tôi, tôi đã bỏ lỡ proc.communicate()sau khi gọi proc.kill(). Điều này làm sạch quá trình stdin, stdout ... và chấm dứt quá trình.


Giải pháp này không hoạt động với tôi trong linux và python 2.7
user9869932 7/12/18

@xyz Nó đã làm việc với tôi trong Linux và python 3.5. Kiểm tra tài liệu cho python 2.7
epinal 10/12/18

@espinal, cảm ơn, vâng. Đây có thể là một vấn đề linux. Đó là linux Raspbian chạy trên Raspberry 3
user9869932

5

Như Sai đã nói, shell là con, vì vậy các tín hiệu bị chặn bởi nó - cách tốt nhất tôi tìm thấy là sử dụng shell = false và sử dụng shlex để tách dòng lệnh:

if isinstance(command, unicode):
    cmd = command.encode('utf8')
args = shlex.split(cmd)

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

Sau đó, p.kill () và p.terminate () sẽ hoạt động như bạn mong đợi.


1
Trong trường hợp của tôi, nó thực sự không giúp ích gì cho cmd đó là "đường dẫn cd && zsync, v.v.". Vì vậy, điều đó thực sự làm cho lệnh thất bại!
dùng175259

4
Sử dụng đường dẫn tuyệt đối thay vì thay đổi thư mục ... Tùy chọn os.chdir (...) vào thư mục đó ...
Matt Billenstein

Khả năng thay đổi thư mục làm việc cho tiến trình con được tích hợp sẵn. Chỉ cần vượt qua cwdđối số để Popen.
Jonathon Reinhart

Tôi đã sử dụng shlex, nhưng vấn đề vẫn tồn tại, giết không phải là giết các tiến trình con.
đóiWolf

1

những gì tôi cảm thấy như chúng ta có thể sử dụng:

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

os.killpg(os.getpgid(pro.pid), signal.SIGINT)

điều này sẽ không giết chết tất cả nhiệm vụ của bạn mà là quá trình với p.pid


0

Tôi biết đây là một câu hỏi cũ nhưng điều này có thể giúp ai đó tìm kiếm một phương pháp khác. Đây là những gì tôi sử dụng trên windows để tiêu diệt các tiến trình mà tôi đã gọi.

si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)

/ IM là tên hình ảnh, bạn cũng có thể làm / PID nếu muốn. / T giết quá trình cũng như các tiến trình con. / Lực F chấm dứt nó. si, như tôi đã thiết lập, là cách bạn làm điều này mà không hiển thị cửa sổ CMD. Mã này được sử dụng trong python 3.


0

Gửi tín hiệu cho tất cả các quy trình trong nhóm

    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

Tôi chưa thấy điều này được đề cập ở đây, vì vậy tôi sẽ đưa nó ra ngoài đó trong trường hợp ai đó cần nó. Nếu tất cả những gì bạn muốn làm là đảm bảo rằng quy trình con của bạn kết thúc thành công, bạn có thể đặt nó trong một trình quản lý bối cảnh. Ví dụ, tôi muốn máy in tiêu chuẩn của mình in ra một hình ảnh và sử dụng trình quản lý bối cảnh đảm bảo rằng quy trình con kết thúc.

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

Tôi tin rằng phương pháp này cũng là đa nền tảng.


Tôi không thấy làm thế nào mã ở đây chấm dứt một quá trình. Bạn có thể làm rõ?
DarkLight

Tôi đã tuyên bố rõ ràng rằng nếu tất cả những gì bạn muốn làm, hãy đảm bảo rằng quy trình của bạn đã chấm dứt trước khi tiếp tục dòng tiếp theo của mã, bạn có thể sử dụng phương pháp này. Tôi không tuyên bố rằng nó chấm dứt quá trình.
Jaiyam Sharma

Nếu người quản lý bối cảnh "đảm bảo rằng quá trình của bạn đã chấm dứt", như bạn đã nêu rõ, sau đó bạn làm tuyên bố rằng nó chấm dứt quá trình này. Liệu nó mặc dù? Ngay cả khi quá trình được đưa ra với shell=True, theo câu hỏi? Mà nó không có trong mã của bạn.
anon

anon, cảm ơn bạn đã chỉ ra cách sử dụng khó hiểu của từ 'chấm dứt'. Trình quản lý bối cảnh chờ cho quá trình con hoàn thành trước khi chuyển sang câu lệnh in trên dòng cuối cùng. Điều này khác với việc chấm dứt ngay lập tức quá trình bằng cách sử dụng thứ gì đó như sigterm. Bạn có thể có kết quả tương tự bằng cách sử dụng một chuỗi và gọi thread.join(). Hi vọng điều này sẽ làm sáng tỏ nó.
Jaiyam Sharma
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.