次のコマンドでサブプロセスを起動します。
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)
しかし、私が使用して殺そうとすると:
p.terminate()
または
p.kill()
コマンドはバックグラウンドで実行され続けるので、どうすれば実際にプロセスを終了させることができるのか疑問に思いました。
このコマンドを次のように実行したときに注意してください。
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
p.terminate()
を発行すると正常に終了します。
グループ内のすべてのプロセスにシグナルを送信できるようにするには、 プロセスグループ を使用します。そのためには、 セッション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
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()
は動作するはずです。
これがあなたのパイプにどのような影響を与えるのか私にはわかりません。
あなたが 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
を使うことです、さもなければあなたが親Shellプロセスを殺すとき、それは現存しないShellプロセスを残すでしょう。
この答えのどれも私のために働いていなかったので、私は働いたコードを残しています。私の場合、.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 ...がクリーンになり、プロセスは終了します。
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()はあなたが期待するように動作するはずです。
私たちが使うことができるように私が感じるもの:
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を持つプロセスを殺すでしょう。
私はこれがここで言及されたのを見たことがないので、誰かがそれを必要とする場合に備えて、それをそこに置いています。サブプロセスが正常に終了することを確認するだけでよい場合は、コンテキストマネージャーに配置できます。たとえば、標準のプリンターでイメージを印刷し、コンテキストマネージャーを使用してサブプロセスが終了するようにしたかったのです。
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...')
この方法はクロスプラットフォームでもあると思います。
私はこれが古い質問であることを知っていますが、これは誰かが違う方法を探すのを助けるかもしれません。これは私が呼び出したプロセスを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で使われています。
グループ内のすべてのプロセスにシグナルを送信する
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)