Pregunta

Estoy lanzando un subproceso con el siguiente comando:

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

Sin embargo, cuando trato de matar usando:

p.terminate()

o

p.kill()

El comando sigue funcionando en segundo plano, por lo que me preguntaba cómo puedo finalizar el proceso.

Tenga en cuenta que cuando ejecuto el comando con:

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

Termina con éxito al emitir el p.terminate().

¿Fue útil?

Solución

Utilizar una grupo de procesos para habilitar el envío de una señal a todo el proceso en los grupos. Para eso, debes adjuntar un ID de sesión al proceso principal de los procesos generados/infantiles, que es un caparazón en su caso. Esto lo convertirá en el líder grupal de los procesos. Entonces, cuando se envía una señal al líder del grupo de procesos, se transmite a todos los procesos infantiles de este grupo.

Aquí está el código:

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

Otros consejos

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

p.kill() termina matando el proceso de concha y cmd sigue funcionando.

Encontré una solución conveniente esto por:

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

Esto hará que la CMD herede el proceso de shell, en lugar de que el shell lance un proceso infantil, que no es asesinado. p.pid será la identificación de su proceso CMD entonces.

p.kill() Deberia trabajar.

Sin embargo, no sé qué efecto tendrá esto en su pipa.

Si puedes usar psutilo, entonces esto funciona perfectamente:

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)

Podría hacerlo usando

from subprocess import Popen

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

mató al cmd.exe y el programa para el que di el comando.

(En Windows)

Cuando shell=True El caparazón es el proceso infantil, y los comandos son sus hijos. Así que cualquiera SIGTERM o SIGKILL Matará el caparazón pero no sus procesos infantiles, y no recuerdo una buena manera de hacerlo. La mejor manera que se me ocurra es usar shell=False, de lo contrario, cuando mate el proceso de la concha principal, dejará un proceso de shell desaparecido.

Ninguna de estas respuestas funcionó para mí, así que dejaré el código que funcionó. En mi caso incluso después de matar el proceso con .kill() y conseguir un .poll() Código de retorno El proceso no terminó.

Siguiendo el subprocess.Popen documentación:

"... para limpiar correctamente una aplicación bien comportada debe matar el proceso del niño y terminar la comunicación ..."

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

En mi caso me estaba perdiendo el proc.communicate() después de llamar proc.kill(). Esto limpia el proceso Stdin, stdout ... y termina el proceso.

Como dijo Sai, el caparazón es el niño, por lo que las señales se interceptan por él; la mejor manera que he encontrado es usar shell = false y usar shlex para dividir la línea de comando:

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

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

Entonces P.Kill () y P.Terminate () deberían funcionar como espere.

Lo que siento que podríamos usar:

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

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

Esto no matará toda su tarea sino el proceso con el P.PID

Sé que esta es una pregunta antigua, pero esto puede ayudar a alguien a buscar un método diferente. Esto es lo que uso en Windows para matar procesos que he llamado.

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

/Im es el nombre de la imagen, también puede hacer /pid si lo desea. /T mata el proceso y los procesos infantiles. /F La fuerza lo termina. Si, como lo tengo establecido, es cómo haces esto sin mostrar una ventana CMD. Este código se usa en Python 3.

Envíe la señal a todos los procesos en grupo

    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)

No he visto esto mencionado aquí, así que lo estoy poniendo en caso de que alguien lo necesite. Si todo lo que desea hacer es asegurarse de que su subproceso termine con éxito, podría ponerlo en un administrador de contexto. Por ejemplo, quería que mi impresora estándar imprimiera una imagen de salida y el uso del Administrador de contexto aseguró que el subproceso finalizara.

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

Creo que este método también es multiplataforma.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top