wake-up-neo.net

So beenden Sie einen Python-Unterprozess, der mit Shell = True gestartet wird

Ich starte einen Subprozess mit dem folgenden Befehl:

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

Jedoch wenn ich versuche zu töten mit:

p.terminate()

oder 

p.kill()

Der Befehl läuft im Hintergrund weiter, also habe ich mich gefragt, wie ich den Vorgang eigentlich beenden kann. 

Beachten Sie Folgendes, wenn ich den Befehl mit:

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

Es wird erfolgreich beendet, wenn p.terminate() ausgegeben wird.

250
user175259

Verwenden Sie eine Prozessgruppe , um das Senden eines Signals an alle Prozesse in den Gruppen zu ermöglichen. Dazu müssen Sie eine session id an den übergeordneten Prozess der erzeugten/untergeordneten Prozesse anhängen, was in Ihrem Fall eine Shell ist. Dies macht es zum Gruppenleiter der Prozesse. Wenn nun ein Signal an den Prozessgruppenleiter gesendet wird, wird es an alle Kindprozesse dieser Gruppe übertragen.

Hier ist der Code:

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
349
mouad
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)
p.kill()

p.kill() beendet den Shell-Prozess und cmd läuft noch.

Ich habe eine bequeme Lösung gefunden durch:

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

Dies führt dazu, dass cmd den Shell-Prozess erbt, anstatt dass die Shell einen untergeordneten Prozess startet, der nicht beendet wird. p.pid ist dann die ID Ihres cmd-Prozesses.

p.kill() sollte funktionieren.

Ich weiß nicht, welche Auswirkungen dies auf Ihre Pfeife haben wird.

80
Bryant Hansen

Wenn Sie psutil verwenden können, funktioniert das perfekt:

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

Ich könnte es mit machen 

from subprocess import Popen

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

es tötete den cmd.exe und das Programm, für das ich den Befehl gab.

(Unter Windows)

19
SPratap

Wenn Shell=True die Shell der untergeordnete Prozess ist und die Befehle ihre untergeordneten sind. Also SIGTERM oder SIGKILL töten die Shell, aber nicht ihre untergeordneten Prozesse, und ich erinnere mich nicht an eine gute Vorgehensweise. Der beste Weg, den ich mir vorstellen kann, ist, Shell=False zu verwenden. Andernfalls wird der übergeordnete Shell-Prozess abgebrochen.

8
Sai Venkat

Keine dieser Antworten hat für mich funktioniert, also lasse ich den Code, der funktioniert hat. In meinem Fall, selbst nachdem der Prozess mit .kill() beendet und ein .poll()-Rückkehrcode erhalten wurde, wurde der Prozess nicht beendet. 

Nach der subprocess.PopenDokumentation :

"... um eine ordnungsgemäße Bereinigung durchzuführen, sollte der untergeordnete Prozess abgebrochen und die Kommunikation beendet werden ..."

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

In meinem Fall fehlte mir nach dem Aufruf von proc.communicate() die proc.kill(). Dies reinigt den Prozess stdin, stdout ... und beendet den Prozess.

6
epinal

Wie Sai gesagt hat, ist die Shell das Kind, also werden Signale von ihr abgefangen - die beste Methode, die ich gefunden habe, ist die Verwendung von Shell = False und die Aufteilung der Befehlszeile mit shlex:

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

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

Dann sollten p.kill () und p.terminate () so funktionieren, wie Sie es erwarten.

5

was ich fühle, wir könnten verwenden:

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

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

dies wird nicht alle Ihre Aufgabe beenden, sondern den Prozess mit der p.pid

1
Nilesh K.

Ich habe das hier nicht gesehen, also stelle ich es da raus, falls jemand es braucht. Wenn Sie nur sicherstellen möchten, dass Ihr Unterprozess erfolgreich beendet wird, können Sie ihn in einen Kontextmanager einfügen. Ich wollte zum Beispiel, dass mein Standarddrucker ein Bild druckt und über den Kontextmanager sicherstellt, dass der Unterprozess beendet wird.

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

Ich glaube, diese Methode ist auch plattformübergreifend.

0
Jaiyam Sharma

Ich weiß, dass dies eine alte Frage ist, aber dies kann jemandem helfen, der nach einer anderen Methode sucht. Dies ist, was ich unter Windows benutze, um Prozesse zu beenden, die ich aufgerufen habe. 

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

/ IM ist der Bildname. Sie können auch/PID verwenden, wenn Sie möchten./T beendet den Prozess sowie die Kindprozesse./F Kraft beendet es. si, wie ich es eingestellt habe, ist, wie Sie dies tun, ohne ein CMD-Fenster anzuzeigen. Dieser Code wird in Python 3 verwendet.

0
Zx4161

Senden Sie das Signal an alle Prozesse in der Gruppe

    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