web-dev-qa-db-ja.com

Shell = Trueで起動したpythonサブプロセスを終了する方法

次のコマンドでサブプロセスを起動します。

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

しかし、私が使用して殺そうとすると:

p.terminate()

または

p.kill()

コマンドはバックグラウンドで実行され続けるので、どうすれば実際にプロセスを終了させることができるのか疑問に思いました。

このコマンドを次のように実行したときに注意してください。

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

p.terminate()を発行すると正常に終了します。

264
user175259

グループ内のすべてのプロセスにシグナルを送信できるようにするには、 プロセスグループ を使用します。そのためには、 セッション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
364
mouad
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プロセスのIDになります。

p.kill()は動作するはずです。

これがあなたのパイプにどのような影響を与えるのか私にはわかりません。

82
Bryant Hansen

あなたが 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)
39
Jovik

私はそれを使ってそれをすることができます

from subprocess import Popen

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

それはcmd.exeと私がコマンドを与えたプログラムを殺しました。

(Windowsの場合)

21
SPratap

Shell=Trueの場合、シェルは子プロセスであり、コマンドはその子です。そのため、SIGTERMSIGKILLはシェルを終了させますが、その子プロセスは終了させません。そのための良い方法は思い出せません。私が考えることができる最善の方法はShell=Falseを使うことです、さもなければあなたが親Shellプロセスを殺すとき、それは現存しないShellプロセスを残すでしょう。

8
Sai Venkat

この答えのどれも私のために働いていなかったので、私は働いたコードを残しています。私の場合、.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 ...がクリーンになり、プロセスは終了します。

6
epinal

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()はあなたが期待するように動作するはずです。

4

私たちが使うことができるように私が感じるもの:

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を持つプロセスを殺すでしょう。

1
Nilesh K.

私はこれがここで言及されたのを見たことがないので、誰かがそれを必要とする場合に備えて、それをそこに置いています。サブプロセスが正常に終了することを確認するだけでよい場合は、コンテキストマネージャーに配置できます。たとえば、標準のプリンターでイメージを印刷し、コンテキストマネージャーを使用してサブプロセスが終了するようにしたかったのです。

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

この方法はクロスプラットフォームでもあると思います。

0
Jaiyam Sharma

私はこれが古い質問であることを知っていますが、これは誰かが違う方法を探すのを助けるかもしれません。これは私が呼び出したプロセスをkillするために私がWindows上で使うものです。

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

/ IMはイメージ名です。必要に応じて/ PIDを指定することもできます。/Tは子プロセスだけでなくプロセスも強制終了します。/F forceで終了します。 siは、私が設定したとおり、CMDウィンドウを表示せずにこれを実行する方法です。このコードはpython 3で使われています。

0
Zx4161

グループ内のすべてのプロセスにシグナルを送信する

    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
roger.wang