web-dev-qa-db-fra.com

Comment mettre fin à un sous-processus python lancé avec Shell = True

Je lance un sous-processus avec la commande suivante:

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

Cependant, quand j'essaye de tuer en utilisant:

p.terminate()

ou 

p.kill()

La commande continue de fonctionner en arrière-plan, je me demandais donc comment puis-je terminer le processus. 

Notez que lorsque je lance la commande avec:

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

Il se termine avec succès lors de l'émission de la p.terminate().

250
user175259

Utilisez un groupe de processus afin d'activer l'envoi d'un signal à tous les processus des groupes. Pour cela, vous devez associer un ID de session au processus parent du processus engendré/enfant, qui est un shell dans votre cas. Cela en fera le chef de groupe des processus. Alors maintenant, lorsqu'un signal est envoyé au responsable du 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
349
mouad
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, Shell=True)
p.kill()

p.kill() finit par tuer le processus Shell et cmd est toujours en cours d'exécution.

J'ai trouvé une solution pratique par:

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

Cela fera en sorte que cmd hérite du processus Shell au lieu que celui-ci lance un processus enfant qui ne soit pas tué. p.pid sera alors l'id de votre processus cmd.

p.kill() devrait fonctionner.

Je ne sais pas quel effet cela aura sur votre pipe si.

80
Bryant Hansen

Si vous pouvez utiliser psutil , cela fonctionnera 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)
39
Jovik

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.

(Sous Windows)

19
SPratap

Lorsque Shell=True, le shell est le processus enfant et les commandes sont ses enfants. Donc, toute SIGTERM ou SIGKILL va tuer Shell mais pas ses processus enfants, et je ne me souviens pas du meilleur moyen de le faire . Le meilleur moyen auquel je puisse penser est d'utiliser Shell=False, sinon, lorsque vous supprimez le processus Shell parent, il laissera un processus Shell obsolète.

8
Sai Venkat

Aucune de ces réponses n'a fonctionné pour moi alors je laisse le code qui a fonctionné. Dans mon cas, même après avoir arrêté le processus avec .kill() et obtenu un code de retour .poll(), le processus ne s'est pas terminé. 

En suivant le subprocess.Popendocumentation :

"... afin de nettoyer correctement une application bien conçue devrait tuer le processus enfant et terminer la communication ..."

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

Dans mon cas, il me manquait la proc.communicate() après avoir appelé proc.kill(). Cela nettoie le processus stdin, stdout ... et met fin au processus.

6
epinal

Comme Sai l'a dit, Shell est l'enfant, ainsi les signaux sont interceptés. Le meilleur moyen que j'ai trouvé est d'utiliser Shell = False et d'utiliser shlex pour fractionner 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)

Ensuite, p.kill () et p.terminate () devraient fonctionner comme vous le souhaitez.

5
Matt Billenstein

ce que je sens 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)

cela ne tuera pas toute votre tâche mais le processus avec le p.pid

1
Nilesh K.

Je n'ai pas vu cela mentionné ici, alors je le publie au cas où quelqu'un en aurait besoin. Si vous souhaitez simplement vous assurer que votre sous-processus se termine correctement, vous pouvez le placer dans un gestionnaire de contexte. Par exemple, je voulais que mon imprimante standard imprime une image externe et, à l'aide du gestionnaire de contexte, m'assurais que le sous-processus se terminait.

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.

0
Jaiyam Sharma

Je sais que c'est une vieille question, mais cela peut aider quelqu'un à la recherche d'une méthode différente. C’est ce que j’utilise sous Windows pour supprimer les processus que j’ai appelés. 

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 aussi faire/PID si vous le souhaitez./T tue le processus ainsi que les processus enfants./F force le termine. Si, comme je l'ai défini, voici comment procéder: sans afficher de fenêtre CMD. Ce code est utilisé dans Python 3.

0
Zx4161

Envoyer le signal à tous les processus du 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)
0
roger.wang