416 votes

Comment terminer 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'essaie de tuer en utilisant :

p.terminate()

ou

p.kill()

La commande continue de s'exécuter en arrière-plan, et je me demande comment je peux mettre fin au 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 commande p.terminate() .

0 votes

Que fait votre cmd ressemble-t-il ? Il peut contenir une commande qui déclenche le démarrage de plusieurs processus. Il n'est donc pas clair de quel processus vous parlez.

2 votes

N'ayant pas shell=True faire une grande différence ?

520voto

mouad Points 21520

Utilisez un groupe de processus afin de permettre l'envoi d'un signal à tous les processus des groupes. Pour cela, vous devez attacher un identifiant de la session au processus parent des processus engendrés/enfants, qui est un shell dans votre cas. Cela en fera le chef de groupe des processus. Donc maintenant, lorsqu'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

2 votes

@PiotrDobrogost : Malheureusement non, car os.setsid n'est pas disponible sous Windows ( docs.python.org/library/os.html#os.setid ), je ne sais pas si cela peut aider mais vous pouvez regarder ici ( bugs.python.org/issue5115 ) pour avoir un aperçu de la manière de procéder.

8 votes

Comment subprocess.CREATE_NEW_PROCESS_GROUP se rapportent à cela ?

0 votes

@PiotrDobrogost : Bien trouvé :), apparemment si tu utilises la fonction subprocess.CREATE_NEW_PROCESS_GROUP flag, vous pouvez créer un groupe de processus dans Window, voyez ici comment : hg.python.org/cpython/file/321414874b26/Lib/test/ Malheureusement, je n'ai pas de machine Windows à ma disposition. Pouvez-vous essayer et me tenir au courant ?)

130voto

Bryant Hansen Points 131
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.

J'ai trouvé une solution pratique :

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

Cela permettra à cmd d'hériter du processus de l'interpréteur de commandes, au lieu que l'interpréteur de commandes lance un processus enfant, qui n'est pas tué. p.pid sera alors l'identifiant de votre processus cmd.

p.kill() devrait fonctionner.

Je ne sais pas quel effet cela aura sur votre tuyau.

2 votes

Une solution agréable et légère pour *nix, merci ! Fonctionne sur Linux, devrait fonctionner sur Mac également.

0 votes

Très bonne solution. Si votre cmd se trouve être un shell script enveloppant pour quelque chose d'autre, appelez le binaire final avec exec aussi afin d'avoir un seul sous-processus.

1 votes

C'est magnifique. J'ai essayé de comprendre comment créer et tuer un sous-processus par espace de travail sur Ubuntu. Cette réponse m'a aidé. J'aimerais pouvoir l'upvoter plus d'une fois.

93voto

Jovik Points 165

Si vous pouvez utiliser psutil alors 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)

3 votes

AttributeError: 'Process' object has no attribute 'get_children pour pip install psutil .

4 votes

Je pense que get_children() devrait être children(). Mais cela n'a pas fonctionné pour moi sous Windows, le processus est toujours là.

3 votes

@Godsmith - L'API de psutil a changé et vous avez raison : enfants() fait la même chose que get_children(). Si cela ne fonctionne pas sous Windows, vous pouvez créer un ticket de bogue dans la section GitHub

40voto

SPratap Points 49

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 pour lequel j'ai donné la commande.

(Sous Windows)

0 votes

Ou utilisez le nom du processus : Popen("TASKKILL /F /IM " + process_name) Si vous ne l'avez pas, vous pouvez l'obtenir sur le site web de la Commission européenne. command paramètre.

9voto

Matt Billenstein Points 502

Comme Sai l'a dit, le shell est l'enfant, donc les signaux sont interceptés par lui -- la meilleure façon que j'ai trouvée est d'utiliser shell=False et d'utiliser shlex 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)

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

1 votes

Dans mon cas, cela n'aide pas vraiment étant donné que cmd est "cd path && zsync etc etc". Cela fait donc échouer la commande !

5 votes

Utiliser les chemins absolus au lieu de changer de répertoire... Optionnellement os.chdir(...) vers ce répertoire...

0 votes

La possibilité de changer le répertoire de travail pour le processus enfant est intégrée. Il suffit de passer l'option cwd argument pour Popen .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X