Comment mettre fin à un sous-python lancé avec shell = True
-
24-10-2019 - |
Question
Je lance un sous-processus avec la commande suivante:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
Cependant, lorsque je tente de tuer en utilisant:
p.terminate()
ou
p.kill()
La commande continue à fonctionner en arrière-plan, donc je me demandais comment puis-je mettre fin à fait le processus.
Notez que lorsque je lance la commande avec:
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
Il ne se termine avec succès lors de l'émission du p.terminate()
.
La solution
Utilisation d'un groupe de processus de manière à permettre l'envoi d'un signal à tous les processus dans les groupes . Pour cela, vous devez joindre une id session, au processus parent du donné naissance / processus enfants, ce qui est une coquille dans votre cas. Il sera le chef de groupe des processus. Alors maintenant, quand un signal est envoyé au chef de groupe de processus, il est transmis à tous les processus enfants de ce groupe.
Voici le 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
Autres conseils
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p.kill()
p.kill()
extrémités par tuer le processus de coquille et cmd
est encore en cours d'exécution.
J'ai trouvé une solution pratique en:
p = subprocess.Popen("exec " + cmd, stdout=subprocess.PIPE, shell=True)
Cela entraînera cmd pour hériter du processus shell, au lieu d'avoir le lancement de la coquille d'un processus enfant, qui ne soit pas tué. p.pid
sera l'ID de votre processus cmd puis.
p.kill()
devrait fonctionner.
Je ne sais pas quel effet cela aura sur votre pipe bien.
Si vous pouvez utiliser psutil , cela fonctionne parfaitement:
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)
Je pourrais le faire en utilisant
from subprocess import Popen
process = Popen(command, shell=True)
Popen("TASKKILL /F /PID {pid} /T".format(pid=process.pid))
il a tué le cmd.exe
et le programme que j'ai donné la commande pour.
(sous Windows)
Lorsque shell=True
la coquille est le processus de l'enfant, et les commandes sont ses enfants. Ainsi, toute SIGTERM
ou SIGKILL
tueront la coque mais non ses processus enfants, et je ne me rappelle pas une bonne façon de le faire.
La meilleure façon que je peux penser est d'utiliser shell=False
, sinon quand vous tuez le processus de shell parent, il laissera un processus shell défunte.
Aucune de ces réponses travaillé pour moi si Im laissant le code qui a fait le travail. Dans mon cas, même après avoir tué le processus avec .kill()
et obtenir un code de retour de .poll()
le processus n'a pas mis fin.
Suite à la subprocess.Popen
documentation :
« ... pour le nettoyage correctement une application bien comportés devrait tuer le processus de l'enfant et de la communication finale ... »
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
Dans mon cas, je manquais le proc.communicate()
après avoir appelé proc.kill()
. Ceci nettoie le processus stdin, stdout ... et ne se termine le processus.
Comme Sai dit, la coquille est l'enfant, de sorte que les signaux sont interceptés par elle - meilleure façon que je l'ai trouvé est d'utiliser shell = False shlex et utiliser pour diviser la ligne de commande:
if isinstance(command, unicode):
cmd = command.encode('utf8')
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Alors p.kill () et p.terminate () devrait fonctionner comment vous vous attendez.
ce que je ressens que nous pourrions utiliser:
import os
import signal
import subprocess
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
os.killpg(os.getpgid(pro.pid), signal.SIGINT)
ce ne sera pas tuer votre tâche, mais le processus avec le p.pid
Je sais que c'est une question ancienne mais quelqu'un d'aide peut la recherche d'une autre méthode. Voilà ce que j'utilise sur les fenêtres pour tuer les processus que je l'ai appelé.
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.call(["taskkill", "/IM", "robocopy.exe", "/T", "/F"], startupinfo=si)
/ IM est le nom de l'image, vous pouvez également faire / PID si vous voulez. / T tue le processus ainsi que les processus enfants. / F Force la termine. si, comme je l'ai mis, comment vous faites cela sans montrer une fenêtre CMD. Ce code est utilisé en Python 3.
Envoyer le signal à tous les processus dans le groupe
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)
Je n'ai pas vu cette mention ici, donc je le mets là au cas où quelqu'un a besoin. Si tout ce que vous voulez faire est de vous assurer que votre sous-processus se termine avec succès, vous pouvez le mettre dans un gestionnaire de contexte. Par exemple, je voulais que mon imprimante standard pour imprimer une image sur et en utilisant le gestionnaire de contexte assuré que le sous-processus terminé.
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...')
Je crois que cette méthode est également multi-plateforme.