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


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 2014年


1
shell=True大きな違いはありませんか?
チャーリーパーカー

回答:


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:残念ながらありません。Windows os.setsidでは使用できないため(docs.python.org/library/os.html#os.setsid)、これが役立つかどうかはわかりませんが、こちらをご覧ください(bugs.python.org / issue5115)方法についての洞察が必要です。
mouad '19

6
これとどのようにsubprocess.CREATE_NEW_PROCESS_GROUP関係していますか?
Piotr Dobrogost '19年

11
私たちのテストでは、setsid!= setpgid、およびos.pgkillは、同じプロセスグループIDを持つサブプロセスのみを強制終了することを示唆しています。プロセスグループが変更されたプロセスは、同じセッションIDを持つ可能性がありますが、
強制終了されませ


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 2015

とても良い解決策です。あなたのcmdが偶然何かのシェルスクリプトラッパーである場合は、サブプロセスを1つだけにするために、execを使用してそこで最終的なバイナリを呼び出してください。
Nicolinux 2016

1
美しいです。私は、Ubuntuのワークスペースごとにサブプロセスを生成して強制終了する方法を理解しようとしています。この答えは私を助けました。私はそれを2回以上
賛成できればいいのに

2
セミコロンが
コマンドで

@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 2015

3
get_children()はchildren()である必要があると思います。しかし、Windowsでは動作しませんでした。プロセスはまだ残っています。
Godsmith

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シェルは、子プロセスであり、コマンドは、その子です。どのだから、SIGTERMまたはSIGKILLシェルではなく、その子プロセスを殺すだろう、と私はそれを行うための良い方法を覚えていません。私が考えることができる最良の方法は、を使用shell=Falseすることです。そうしないと、親シェルプロセスを強制終了すると、無効なシェルプロセスが残ります。


8

この答えはどれもうまくいきませんでした。私の場合、プロセスを強制終了し.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では動作しました。python 2.7のドキュメントを確認
epinal

@espinal、ありがとう、はい。おそらくLinuxの問題です。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"であることを考えると、役に立ちません。そのため、実際にはコマンドが失敗します!
user175259 2011年

4
ディレクトリを変更する代わりに絶対パスを使用します...オプションでそのディレクトリへのos.chdir(...)...
Matt Billenstein

子プロセスの作業ディレクトリを変更する機能が組み込まれています。cwd引数をに渡すだけPopenです。
Jonathon Reinhart、2015

私はshlexを使用しましたが、それでも問題は解決しません、killは子プロセスをkillしていません。
hungryWolf 2017年

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 forceはそれを終了します。私が設定しているように、siはCMDウィンドウを表示せずにこれを行う方法です。このコードはPython 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、「終了」という言葉の紛らわしい用法を指摘してくれてありがとう。コンテキストマネージャは、サブプロセスが完了するのを待ってから、最終行の印刷ステートメントに進みます。これは、sigtermなどを使用してプロセスを即座に終了することとは異なります。スレッドを使用してを呼び出しても同じ結果が得られthread.join()ます。これで問題が解決することを願っています。
Jaiyam Sharma
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.