Utilizando el módulo «subproceso» con tiempo de espera

Aquí está el código en Python para ejecutar un comando arbitrario de regresar a su stdout de datos, o de levantar una excepción en la no-cero códigos de salida:

proc = subprocess.Popen(
    cmd,
    stderr=subprocess.STDOUT,  # Merge stdout and stderr
    stdout=subprocess.PIPE,
    shell=True)

communicate se utiliza para esperar a que el proceso para salir:

stdoutdata, stderrdata = proc.communicate()

La subprocess módulo no es compatible con tiempo de espera–capacidad para matar un proceso que se ejecuta durante más de X número de segundos, por lo tanto, communicate puede tomar para siempre a ejecutar.

¿Cuál es la más simple forma de implementar los tiempos de espera en un programa en Python diseñado para funcionar en Windows y Linux?

  • Una relacionada con Python issue tracker de entrada: bugs.python.org/issue5673
  • El uso de pypi.python.org/pypi/subprocess32 para Python2.x. Es una migración de Python 3.x. Tiene el tiempo de espera de argumento para call() y wait().
  • pypi.python.org/pypi/subprocess32 no funciona en Windows 🙁

29 Kommentare

  1. 149

    En Python 3.3+:

    from subprocess import STDOUT, check_output
    
    output = check_output(cmd, stderr=STDOUT, timeout=seconds)

    output es una cadena de bytes que contiene el comando fusionado stdout, stderr datos.

    check_output plantea CalledProcessError de salida distinto de cero de estado tal como se especifica en la pregunta del texto, a diferencia de proc.communicate() método.

    Me he quitado shell=True porque se utiliza a menudo innecesariamente. Siempre se puede agregar de nuevo si cmd de hecho así lo requiere. Si agrega shell=True es decir, si el niño proceso genera sus propios descendientes; check_output() puede volver mucho más tarde que el tiempo de espera indica, ver Subproceso error de tiempo de espera.

    La función de tiempo de espera está disponible en Python 2.x a través de la subprocess32 migración de la 3.2+ módulo subprocess.

    • De hecho, y subproceso de tiempo de espera de apoyo que existe en la subprocess32 backport que mantener para su uso en Python 2. pypi.python.org/pypi/subprocess32
    • Sridhar pidió cruz solución de la plataforma, mientras que su backport sólo admite POSIX : cuando lo probé, MSVC se quejó (espera) acerca de la falta de unistd.h 🙂
    • Si usted no necesita la salida, sólo se puede utilizar el subproceso.de la llamada.
    • +1 solo por el shell=True comentario.
  2. 200

    No sé mucho acerca de los detalles; pero, dado que en
    python 2.6 la API ofrece la capacidad de esperar para hilos y
    terminar los procesos, lo que acerca de la ejecución del proceso por separado, en un
    hilo?

    import subprocess, threading
    
    class Command(object):
        def __init__(self, cmd):
            self.cmd = cmd
            self.process = None
    
        def run(self, timeout):
            def target():
                print 'Thread started'
                self.process = subprocess.Popen(self.cmd, shell=True)
                self.process.communicate()
                print 'Thread finished'
    
            thread = threading.Thread(target=target)
            thread.start()
    
            thread.join(timeout)
            if thread.is_alive():
                print 'Terminating process'
                self.process.terminate()
                thread.join()
            print self.process.returncode
    
    command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
    command.run(timeout=3)
    command.run(timeout=1)

    La salida de este fragmento de código en mi máquina es:

    Thread started
    Process started
    Process finished
    Thread finished
    0
    Thread started
    Process started
    Terminating process
    Thread finished
    -15

    donde se puede observar que, en la primera ejecución, el proceso de
    terminado correctamente (código de retorno 0), mientras que en el segundo, la
    el proceso fue terminado (código de retorno -15).

    Yo no lo he probado en windows; pero, aparte de actualizar el ejemplo
    comando, creo que debería de funcionar ya que no he encontrado en el
    documentación de nada de lo que dice ese hilo.unirse o proceso.terminar
    no es compatible.

    • +1 Por ser independiente de la plataforma. Me he encontrado esto en linux y windows 7 (cygwin y la llanura de windows python) — funciona como se esperaba en los tres casos.
    • He modificado el código un poco el fin de ser capaz de pasar nativo Popen kwargs y lo puso en lo esencial. Ahora está listo para usar multi propósito; gist.github.com/1306188
    • Yo estoy usando esta clase en otro hilo, el problema es que, cuando el comando externo de retrasos y de python intenta llamar a proceso.terminar, la variable de proceso se establece a Ninguno. Miré por la situación en la Popen retorno s Ninguno, pero no pude encontrar ninguna información.
    • No funciona en Windows7. El script se detuvo, pero no el comando. por ejemplo, «ping http://www.redicecn.com -t»
    • Para cualquier persona que tiene el problema @redice estaba teniendo, esta pregunta puede ayudar. En resumen, si el uso de shell=True, el shell se convierte en el proceso hijo que muere, y su comando (hijo de el proceso hijo) vive en.
    • Esta respuesta no proporciona la misma funcionalidad que el original ya no volver a stdout.
    • también, esto no funciona cuando un proceso se detiene en terminar (por ejemplo, ffmpeg en algunas situaciones). va a permanecer para siempre en ‘Terminar proceso’. para este caso específico se tiene que emitir .terminate() dos veces, pero eso no es una solución general. todavía estoy buscando uno
    • subproceso.is_alive puede conducir a una condición de carrera. Consulte ostricher.com/2015/01/python-subprocess-with-timeout
    • usted podría evitar la condición de anticipación llamando p.kill() incondicionalmente (EAFP vs LBYL)
    • Básicamente, esto funciona, pero lo que falta es el código para matar el árbol de procesos que se requiere en el caso de shell=True (el shell se convierte en un proceso secundario). Para responder a Anson las preguntas que voy a publicar que la solución más abajo en este hilo.
    • Windows cygwin puede necesitar más potencia de frenado. En lugar de auto.proceso.terminate(), esta solución trabajado: subprocess.Popen("TASKKILL /F /PID {pid} /T".format(pid=self.process.pid))

  3. 120

    jcollado la respuesta puede ser simplificado con el uso de la las operaciones de roscado.Temporizador clase:

    import shlex
    from subprocess import Popen, PIPE
    from threading import Timer
    
    def run(cmd, timeout_sec):
        proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
        timer = Timer(timeout_sec, proc.kill)
        try:
            timer.start()
            stdout, stderr = proc.communicate()
        finally:
            timer.cancel()
    
    # Examples: both take 1 second
    run("sleep 1", 5)  # process ends normally at 1 second
    run("sleep 5", 1)  # timeout happens at 1 second
    • +1 para la simple solución portátil. Usted no necesita lambda: t = Timer(timeout, proc.kill)
    • +1 Esto debe ser aceptado respuesta, ya que no requiere la manera en que el proceso está en marcha para ser cambiado.
    • Por qué requiere la lambda? No podía el método vinculado p.matan a ser utilizado sin la lambda no?
    • // , Estaría dispuesto a incluir un ejemplo del uso de este?
    • gran solución, evitando is_alive
    • jcollado la respuesta que funciona para mí, pero este no, en Win7
    • Esto funciona para mí, pero todavía tengo la costumbre de Windows comportamiento de solo matar el generado proceso padre y no la de sus hijos. Pero eso es independiente.
    • No funciona cuando la shell=True se utiliza en Popen, podría alguien explicar por favor ?
    • Después de probar este código exacto con mucho más corto temporizadores, he encontrado un problema. El «fin» de la declaración que contiene la «temporizador.cancelar()» de la llamada, debe ser envuelto en su propio try/except. En casos raros, yo era capaz de crear con un Error muy corto temporizadores cuando el Proceso había sido asesinado justo antes de la ejecución del código se podría ejecutar. | finalmente: tratar: el temporizador.cancelar() excepto OSError como e: si » No hay tal proceso no en str(e): raise OSError(e) Hmmm. en realidad, todavía soy capaz de activarlo, y por alguna razón, no he sido capaz de atraparlo. investigando más,…
    • ¿Cómo saber si el proceso que terminó con normalidad o se ha agotado ?

  4. 80

    Si usted está en Unix,

    import signal
      ...
    class Alarm(Exception):
        pass
    
    def alarm_handler(signum, frame):
        raise Alarm
    
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(5*60)  # 5 minutes
    try:
        stdoutdata, stderrdata = proc.communicate()
        signal.alarm(0)  # reset the alarm
    except Alarm:
        print "Oops, taking too long!"
        # whatever else
    • Bueno, yo estoy interesado en una cruz-plataforma de solución que funciona, al menos en win/linux/mac.
    • Me gusta esta basado en unix enfoque. Idealmente, uno podría combinar esto con un enfoque específico (mediante CreateProcess y Puestos de trabajo) .. pero por ahora, la solución a continuación es simple, fácil y funciona muy lejanos.
    • He añadido una solución portátil, ver mi respuesta
    • Esta solución de trabajo de only_if de la señal.la señal(la señal.SIGALARM, alarm_handler) se llama desde el hilo principal. Consulte la documentación de la señal
    • Por desgracia, cuando se ejecuta (en linux) en el contexto de un módulo de Apache (como mod_python, mod_perl, o mod_php), he encontrado que el uso de las señales y alarmas a ser rechazado (presumiblemente debido a que interfieren con Apache propio IPC de la lógica). Así que para lograr el objetivo de que se agote el tiempo de un comando me he visto obligado a escribir «padres » loops» que iniciar un proceso secundario y, a continuación, sentarse en un «sueño»y de bucle mirar el reloj (y posiblemente también la salida de monitorización de el niño).
    • No tiene que cerrar stdoutdata y stderrdata en un finally cláusula, a menos que usted tenga un subprocess.Popen(..., close_fds=True)
    • Me pregunto por qué la Alarma está bajo la Excepción más bien que bajo EnvironmentError. Parece que el proceso de estancamiento es conceptualmente similar a diversas OS errores.
    • Bonita respuesta. Sin embargo, tenga cuidado de que esta solución sólo se muestra el tiempo de espera, sin parar/matar el proceso. Bueno yo tenía 8 núcleos disponibles.

  5. 43

    Aquí es Alex Martelli la solución como un módulo con un adecuado proceso de matanza. Los otros métodos no funcionan debido a que no utilizan proc.comunicarse(). Así que si usted tiene un proceso que produce muchos de salida, va a llenar el búfer de salida y, a continuación, bloquear hasta que leer algo de él.

    from os import kill
    from signal import alarm, signal, SIGALRM, SIGKILL
    from subprocess import PIPE, Popen
    
    def run(args, cwd = None, shell = False, kill_tree = True, timeout = -1, env = None):
        '''
        Run a command with a timeout after which it will be forcibly
        killed.
        '''
        class Alarm(Exception):
            pass
        def alarm_handler(signum, frame):
            raise Alarm
        p = Popen(args, shell = shell, cwd = cwd, stdout = PIPE, stderr = PIPE, env = env)
        if timeout != -1:
            signal(SIGALRM, alarm_handler)
            alarm(timeout)
        try:
            stdout, stderr = p.communicate()
            if timeout != -1:
                alarm(0)
        except Alarm:
            pids = [p.pid]
            if kill_tree:
                pids.extend(get_process_children(p.pid))
            for pid in pids:
                # process might have died before getting to this line
                # so wrap to avoid OSError: no such process
                try: 
                    kill(pid, SIGKILL)
                except OSError:
                    pass
            return -9, '', ''
        return p.returncode, stdout, stderr
    
    def get_process_children(pid):
        p = Popen('ps --no-headers -o pid --ppid %d' % pid, shell = True,
                  stdout = PIPE, stderr = PIPE)
        stdout, stderr = p.communicate()
        return [int(p) for p in stdout.split()]
    
    if __name__ == '__main__':
        print run('find /', shell = True, timeout = 3)
        print run('find', shell = True)
    • Esto no funcionará en windows, además de la orden de funciones se invierte.
    • Esto a veces resulta en la excepción cuando otro controlador registra a sí mismo en SIGALARM y mata el proceso antes de que éste llegue a «matar», agregó el trabajo-alrededor de. Por CIERTO, gran receta! La he usado para el lanzamiento de 50.000 buggy procesos tan lejos sin la congelación o estrellarse el manejo de contenedor.
    • Cómo puede ser modificado para correr en un Subproceso de la aplicación? Estoy tratando de utilizar desde dentro de subprocesos de trabajo y obtener ValueError: signal only works in main thread
    • Bulatov Gracias por la info. ¿Cuál fue la solución que has añadido a lidiar con el problema mencionado?
    • Sólo agregó que «la prueba;la captura de» bloquear, es dentro del código. Por CIERTO, en el largo plazo, esto resultó que me dan problemas, ya que sólo se puede establecer una SIGALARM controlador, y otros procesos de reset. Una solución a este es dada aquí — stackoverflow.com/questions/6553423/…
  6. 17

    He modificado sussudio respuesta. Función ahora devuelve: (returncode, stdout, stderr, timeout) – stdout y stderr es decodificado a la cadena utf-8

    def kill_proc(proc, timeout):
      timeout["value"] = True
      proc.kill()
    
    def run(cmd, timeout_sec):
      proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
      timeout = {"value": False}
      timer = Timer(timeout_sec, kill_proc, [proc, timeout])
      timer.start()
      stdout, stderr = proc.communicate()
      timer.cancel()
      return proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"), timeout["value"]
  7. 17

    sorprendió a nadie menciona el uso de timeout

    timeout 5 ping -c 3 somehost

    Esto no para de trabajo para cada caso de uso, obviamente, pero si su tratando con un simple script, esto es difícil de superar.

    También disponible como gtimeout en coreutils a través de homebrew para los usuarios de mac.

    • usted quiere decir: proc = subprocess.Popen(['/usr/bin/timeout', str(timeout)] + cmd, ...). Hay timeout de comandos en Windows como el OP pregunta?
    • En windows, se puede utilizar la aplicación como git bash, que permite bash utilidades en Windows.
    • incluso si usted usa git bash, cuando python llamadas subproceso se ejecuta en Windows, por lo tanto, esta derivación no funciona.
  8. 10

    Otra opción es escribir en un archivo temporal para evitar el stdout de bloqueo en lugar de necesitar encuesta de comunicarse(). Esto funcionó para mí, donde las otras respuestas no; por ejemplo en windows.

        outFile =  tempfile.SpooledTemporaryFile() 
        errFile =   tempfile.SpooledTemporaryFile() 
        proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
        wait_remaining_sec = timeout
    
        while proc.poll() is None and wait_remaining_sec > 0:
            time.sleep(1)
            wait_remaining_sec -= 1
    
        if wait_remaining_sec <= 0:
            killProc(proc.pid)
            raise ProcessIncompleteError(proc, timeout)
    
        # read temp streams from start
        outFile.seek(0);
        errFile.seek(0);
        out = outFile.read()
        err = errFile.read()
        outFile.close()
        errFile.close()
    • Parece incompleta – ¿qué es tempfile?
    • Incluir «importar tempfile», «el tiempo de importación» y «shell=True» en el interior «Popen» llame al (ten cuidado con el «shell=True»)!
  9. 10

    tiempo de espera es ahora compatible por call() y communicate() en el módulo subprocess (como de Python3.3):

    import subprocess
    
    subprocess.call("command", timeout=20, shell=True)

    Este se llame el comando y elevar la excepción

    subprocess.TimeoutExpired

    si el comando no finalizar después de 20 segundos.

    Puede entonces controlar la excepción para continuar con su código, algo así como:

    try:
        subprocess.call("command", timeout=20, shell=True)
    except subprocess.TimeoutExpired:
        # insert code here

    Espero que esto ayude.

  10. 8

    No sé por qué no se menciona, pero dado que Python 3.5, hay un nuevo subproceso.ejecutar universal de comandos (que está destinado a reemplazar check_call, check_output …) y que tiene el timeout parámetro.

    subproceso.ejecutar(args, *, stdin=Ninguno, de entrada=Ninguno, stdout=Ninguno, stderr=Ninguno, shell=False, cwd=Ninguno, tiempo de espera=Ninguno, check=False, encoding=Ninguno, errores=None)

    Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

    Se plantea una subprocess.TimeoutExpired excepción cuando el tiempo de espera ha caducado.

  11. 5

    Aquí está mi solución, yo estaba usando el Hilo y Eventos:

    import subprocess
    from threading import Thread, Event
    
    def kill_on_timeout(done, timeout, proc):
        if not done.wait(timeout):
            proc.kill()
    
    def exec_command(command, timeout):
    
        done = Event()
        proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
        watcher = Thread(target=kill_on_timeout, args=(done, timeout, proc))
        watcher.daemon = True
        watcher.start()
    
        data, stderr = proc.communicate()
        done.set()
    
        return data, stderr, proc.returncode

    En acción:

    In [2]: exec_command(['sleep', '10'], 5)
    Out[2]: ('', '', -9)
    
    In [3]: exec_command(['sleep', '10'], 11)
    Out[3]: ('', '', 0)
  12. 4

    La solución que uso es el prefijo del comando de shell con timelimit. Si el comando toma demasiado tiempo, timelimit va a parar y Popen tendrá un returncode establecido por límite de tiempo. Si es > 128, significa límite de tiempo muerto en el proceso.

    Ver también python subproceso con tiempo de espera y de salida grande (>64 KB)

  13. 4

    He añadido la solución con el roscado de jcollado a mi módulo de Python easyprocess.

    Instalar:

    pip install easyprocess

    Ejemplo:

    from easyprocess import Proc
    
    # shell is not supported!
    stdout=Proc('ping localhost').call(timeout=1.5).stdout
    print stdout
  14. 4

    Anteponiendo el comando Linux timeout no es una mala solución, y que trabajó para mí.

    cmd = "timeout 20 "+ cmd
    subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (output, err) = p.communicate()
  15. 3

    si usted está usando python 2, darle una oportunidad

    import subprocess32
    
    try:
        output = subprocess32.check_output(command, shell=True, timeout=3)
    except subprocess32.TimeoutExpired as e:
        print e
    • Probablemente no funciona en Windows, como se pide en la pregunta inicial
  16. 2

    He implementado lo que he podido recopilar de un par de estos. Esto funciona en Windows, y ya que esto es una wiki de la comunidad, se me figura que me gustaría compartir mi código:

    class Command(threading.Thread):
        def __init__(self, cmd, outFile, errFile, timeout):
            threading.Thread.__init__(self)
            self.cmd = cmd
            self.process = None
            self.outFile = outFile
            self.errFile = errFile
            self.timed_out = False
            self.timeout = timeout
    
        def run(self):
            self.process = subprocess.Popen(self.cmd, stdout = self.outFile, \
                stderr = self.errFile)
    
            while (self.process.poll() is None and self.timeout > 0):
                time.sleep(1)
                self.timeout -= 1
    
            if not self.timeout > 0:
                self.process.terminate()
                self.timed_out = True
            else:
                self.timed_out = False

    Luego de otra clase o de archivo:

            outFile =  tempfile.SpooledTemporaryFile()
            errFile =   tempfile.SpooledTemporaryFile()
    
            executor = command.Command(c, outFile, errFile, timeout)
            executor.daemon = True
            executor.start()
    
            executor.join()
            if executor.timed_out:
                out = 'timed out'
            else:
                outFile.seek(0)
                errFile.seek(0)
                out = outFile.read()
                err = errFile.read()
    
            outFile.close()
            errFile.close()
    • En realidad, esto probablemente no funciona. El terminate() función de las marcas de un hilo como ha terminado, pero en realidad no interrumpir el hilo! Puedo comprobar esto en *nix, pero no tengo un equipo Windows a prueba.
  17. 2

    Una vez que usted entienda pleno proceso de ejecución de la maquinaria *unix, usted va a encontrar fácilmente la solución más simple:

    Considerar este ejemplo simple de cómo hacer timeoutable comunicarse() meth el uso de select.seleccione() (disponible alsmost everythere en *nix en la actualidad). Esto también puede ser escrito con epoll/encuesta/kqueue, pero seleccionar.seleccione() la variante podría ser un buen ejemplo para ti. Y de las principales limitaciones de seleccionar.seleccione() (velocidad y 1024 max fds) no son applicapable para su tarea.

    Esto funciona en *nix, no crear hilos, no utiliza señales, puede ser inicializado desde cualquier hilo (no sólo el principal), rápida y suficiente para leer los 250 mb/s de datos de salida estándar (stdout) en mi máquina (i5 2.3 ghz).

    Hay un problema en unirse a ing stdout/stderr al final de comunicarse. Si usted tiene enorme programa de salida, esto podría conducir a gran uso de memoria. Pero usted puede llamar a comunicarse() varias veces con menores tiempos de espera.

    class Popen(subprocess.Popen):
        def communicate(self, input=None, timeout=None):
            if timeout is None:
                return subprocess.Popen.communicate(self, input)
    
            if self.stdin:
                # Flush stdio buffer, this might block if user
                # has been writing to .stdin in an uncontrolled
                # fashion.
                self.stdin.flush()
                if not input:
                    self.stdin.close()
    
            read_set, write_set = [], []
            stdout = stderr = None
    
            if self.stdin and input:
                write_set.append(self.stdin)
            if self.stdout:
                read_set.append(self.stdout)
                stdout = []
            if self.stderr:
                read_set.append(self.stderr)
                stderr = []
    
            input_offset = 0
            deadline = time.time() + timeout
    
            while read_set or write_set:
                try:
                    rlist, wlist, xlist = select.select(read_set, write_set, [], max(0, deadline - time.time()))
                except select.error as ex:
                    if ex.args[0] == errno.EINTR:
                        continue
                    raise
    
                if not (rlist or wlist):
                    # Just break if timeout
                    # Since we do not close stdout/stderr/stdin, we can call
                    # communicate() several times reading data by smaller pieces.
                    break
    
                if self.stdin in wlist:
                    chunk = input[input_offset:input_offset + subprocess._PIPE_BUF]
                    try:
                        bytes_written = os.write(self.stdin.fileno(), chunk)
                    except OSError as ex:
                        if ex.errno == errno.EPIPE:
                            self.stdin.close()
                            write_set.remove(self.stdin)
                        else:
                            raise
                    else:
                        input_offset += bytes_written
                        if input_offset >= len(input):
                            self.stdin.close()
                            write_set.remove(self.stdin)
    
                # Read stdout /stderr by 1024 bytes
                for fn, tgt in (
                    (self.stdout, stdout),
                    (self.stderr, stderr),
                ):
                    if fn in rlist:
                        data = os.read(fn.fileno(), 1024)
                        if data == '':
                            fn.close()
                            read_set.remove(fn)
                        tgt.append(data)
    
            if stdout is not None:
                stdout = ''.join(stdout)
            if stderr is not None:
                stderr = ''.join(stderr)
    
            return (stdout, stderr)
    • Esto se refiere sólo a la de Unix mitad del problema.
  18. 1

    Aunque no he mirado extensamente, este decorador que he encontrado en ActiveState parece ser bastante útil para este tipo de cosas. Junto con subprocess.Popen(..., close_fds=True), al menos yo estoy listo para la shell-scripting en Python.

    • Este decorador usos de la señal.alarma, que no está disponible en Windows.
  19. 1

    Hay una idea para crear una subclase de la Popen clase y extender con un simple método de decoradores. Vamos a llamar a ExpirablePopen.

    from logging import error
    from subprocess import Popen
    from threading import Event
    from threading import Thread
    
    
    class ExpirablePopen(Popen):
    
        def __init__(self, *args, **kwargs):
            self.timeout = kwargs.pop('timeout', 0)
            self.timer = None
            self.done = Event()
    
            Popen.__init__(self, *args, **kwargs)
    
        def __tkill(self):
            timeout = self.timeout
            if not self.done.wait(timeout):
                error('Terminating process {} by timeout of {} secs.'.format(self.pid, timeout))
                self.kill()
    
        def expirable(func):
            def wrapper(self, *args, **kwargs):
                # zero timeout means call of parent method
                if self.timeout == 0:
                    return func(self, *args, **kwargs)
    
                # if timer is None, need to start it
                if self.timer is None:
                    self.timer = thr = Thread(target=self.__tkill)
                    thr.daemon = True
                    thr.start()
    
                result = func(self, *args, **kwargs)
                self.done.set()
    
                return result
            return wrapper
    
        wait = expirable(Popen.wait)
        communicate = expirable(Popen.communicate)
    
    
    if __name__ == '__main__':
        from subprocess import PIPE
    
        print ExpirablePopen('ssh -T [email protected]', stdout=PIPE, timeout=1).communicate()
  20. 1

    Usted puede hacer esto utilizando select

    import subprocess
    from datetime import datetime
    from select import select
    
    def call_with_timeout(cmd, timeout):
        started = datetime.now()
        sp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        while True:
            p = select([sp.stdout], [], [], timeout)
            if p[0]:
                p[0][0].read()
            ret = sp.poll()
            if ret is not None:
                return ret
            if (datetime.now()-started).total_seconds() > timeout:
                sp.kill()
                return None
  21. 1

    Yo tenía el problema de que quería terminar de subprocesamiento múltiple subproceso si se tardó más de un determinado tiempo de espera de longitud. Quería establecer un tiempo de espera en Popen(), pero no funcionó. Entonces, me di cuenta de que Popen().wait() es igual a call() y entonces tuve la idea de establecer un tiempo de espera en la .wait(timeout=xxx) método, que finalmente se trabajó. Por lo tanto, he resuelto de esta manera:

    import os
    import sys
    import signal
    import subprocess
    from multiprocessing import Pool
    
    cores_for_parallelization = 4
    timeout_time = 15  # seconds
    
    def main():
        jobs = [...YOUR_JOB_LIST...]
        with Pool(cores_for_parallelization) as p:
            p.map(run_parallel_jobs, jobs)
    
    def run_parallel_jobs(args):
        # Define the arguments including the paths
        initial_terminal_command = 'C:\\Python34\\python.exe'  # Python executable
        function_to_start = 'C:\\temp\\xyz.py'  # The multithreading script
        final_list = [initial_terminal_command, function_to_start]
        final_list.extend(args)
    
        # Start the subprocess and determine the process PID
        subp = subprocess.Popen(final_list)  # starts the process
        pid = subp.pid
    
        # Wait until the return code returns from the function by considering the timeout. 
        # If not, terminate the process.
        try:
            returncode = subp.wait(timeout=timeout_time)  # should be zero if accomplished
        except subprocess.TimeoutExpired:
            # Distinguish between Linux and Windows and terminate the process if 
            # the timeout has been expired
            if sys.platform == 'linux2':
                os.kill(pid, signal.SIGTERM)
            elif sys.platform == 'win32':
                subp.terminate()
    
    if __name__ == '__main__':
        main()
  22. 0

    Por desgracia, estoy obligado por políticas muy estrictas sobre la divulgación del código fuente por parte de mi empleador, por lo que no puede proporcionar el código real. Pero para mi gusto la mejor solución es crear una subclase primordial Popen.wait() a la encuesta en lugar de esperar indefinidamente, y Popen.__init__ a aceptar un parámetro de tiempo de espera. Una vez hecho eso, todos los demás Popen métodos (que se llaman a wait) funcionará como se esperaba, incluyendo communicate.

  23. 0

    https://pypi.python.org/pypi/python-subprocess2 proporciona extensiones del módulo subprocess que permiten a esperar hasta un cierto período de tiempo, terminar de otra manera.

    Así que, a esperar 10 segundos para la terminación del proceso, de lo contrario matar:

    pipe  = subprocess.Popen('...')
    
    timeout =  10
    
    results = pipe.waitOrTerminate(timeout)

    Esto es compatible con windows y unix. «resultados» es un diccionario, que contiene «returnCode», que es el regreso de la aplicación (o Ninguno si es que tenía que ser asesinado), así como «actionTaken». que será «SUBPROCESS2_PROCESS_COMPLETED» si el proceso se completó con normalidad, o una máscara de «SUBPROCESS2_PROCESS_TERMINATED» y SUBPROCESS2_PROCESS_KILLED en función de las medidas adoptadas (consulte la documentación para obtener los detalles completos)

  24. 0

    Esta solución elimina el proceso de árboles en caso de shell=True, pasa los parámetros del proceso (o no), tiene un tiempo de espera y recibe el stdout, stderr y el proceso de salida de la llamada (usa psutil para la kill_proc_tree). Esto se basa en varios soluciones publicado en TAN incluyendo jcollado del. La publicación en respuesta a los comentarios de Anson y jradice en jcollado la respuesta. Probado en Windows Srvr 2012 y Ubuntu 14.04. Por favor, tenga en cuenta que para Ubuntu necesita cambiar la de los padres.los niños(…) llamar a los padres.get_children(…).

    def kill_proc_tree(pid, including_parent=True):
      parent = psutil.Process(pid)
      children = parent.children(recursive=True)
      for child in children:
        child.kill()
      psutil.wait_procs(children, timeout=5)
      if including_parent:
        parent.kill()
        parent.wait(5)
    
    def run_with_timeout(cmd, current_dir, cmd_parms, timeout):
      def target():
        process = subprocess.Popen(cmd, cwd=current_dir, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    
        # wait for the process to terminate
        if (cmd_parms == ""):
          out, err = process.communicate()
        else:
          out, err = process.communicate(cmd_parms)
        errcode = process.returncode
    
      thread = Thread(target=target)
      thread.start()
    
      thread.join(timeout)
      if thread.is_alive():
        me = os.getpid()
        kill_proc_tree(me, including_parent=False)
        thread.join()
  25. 0

    para python 2.6+, uso gevent

     from gevent.subprocess import Popen, PIPE, STDOUT
    
     def call_sys(cmd, timeout):
          p= Popen(cmd, shell=True, stdout=PIPE)
          output, _ = p.communicate(timeout=timeout)
          assert p.returncode == 0, p. returncode
          return output
    
     call_sys('./t.sh', 2)
    
     # t.sh example
     sleep 5
     echo done
     exit 1
  26. 0

    python 2.7

    import time
    import subprocess
    
    def run_command(cmd, timeout=0):
        start_time = time.time()
        df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        while timeout and df.poll() == None:
            if time.time()-start_time >= timeout:
                df.kill()
                return -1, ""
        output = '\n'.join(df.communicate()).strip()
        return df.returncode, output
  27. -1
    import subprocess, optparse, os, sys, re, datetime, threading, time, glob, shutil, xml.dom.minidom, traceback
    
    class OutputManager:
        def __init__(self, filename, mode, console, logonly):
            self.con = console
            self.logtoconsole = True
            self.logtofile = False
    
            if filename:
                try:
                    self.f = open(filename, mode)
                    self.logtofile = True
                    if logonly == True:
                        self.logtoconsole = False
                except IOError:
                    print (sys.exc_value)
                    print ("Switching to console only output...\n")
                    self.logtofile = False
                    self.logtoconsole = True
    
        def write(self, data):
            if self.logtoconsole == True:
                self.con.write(data)
            if self.logtofile == True:
                self.f.write(data)
            sys.stdout.flush()
    
    def getTimeString():
            return time.strftime("%Y-%m-%d", time.gmtime())
    
    def runCommand(command):
        '''
        Execute a command in new thread and return the
        stdout and stderr content of it.
        '''
        try:
            Output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
        except Exception as e:
            print ("runCommand failed :%s" % (command))
            print (str(e))
            sys.stdout.flush()
            return None
        return Output
    
    def GetOs():
        Os = ""
        if sys.platform.startswith('win32'):
            Os = "win"
        elif sys.platform.startswith('linux'):
            Os = "linux"
        elif sys.platform.startswith('darwin'):
            Os = "mac"
        return Os
    
    
    def check_output(*popenargs, **kwargs):
        try:
            if 'stdout' in kwargs: 
                raise ValueError('stdout argument not allowed, it will be overridden.') 
    
            # Get start time.
            startTime = datetime.datetime.now()
            timeoutValue=3600
    
            cmd = popenargs[0]
    
            if sys.platform.startswith('win32'):
                process = subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True) 
            elif sys.platform.startswith('linux'):
                process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 
            elif sys.platform.startswith('darwin'):
                process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 
    
            stdoutdata, stderrdata = process.communicate( timeout = timeoutValue )
            retcode = process.poll()
    
            ####################################
            # Catch crash error and log it.
            ####################################
            OutputHandle = None
            try:
                if retcode >= 1:
                    OutputHandle = OutputManager( 'CrashJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                    OutputHandle.write( cmd )
                    print (stdoutdata)
                    print (stderrdata)
                    sys.stdout.flush()
            except Exception as e:
                print (str(e))
    
        except subprocess.TimeoutExpired:
                ####################################
                # Catch time out error and log it.
                ####################################
                Os = GetOs()
                if Os == 'win':
                    killCmd = "taskkill /FI \"IMAGENAME eq {0}\" /T /F"
                elif Os == 'linux':
                    killCmd = "pkill {0)"
                elif Os == 'mac':
                    # Linux, Mac OS
                    killCmd = "killall -KILL {0}"
    
                runCommand(killCmd.format("java"))
                runCommand(killCmd.format("YouApp"))
    
                OutputHandle = None
                try:
                    OutputHandle = OutputManager( 'KillJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                    OutputHandle.write( cmd )
                except Exception as e:
                    print (str(e))
        except Exception as e:
                for frame in traceback.extract_tb(sys.exc_info()[2]):
                            fname,lineno,fn,text = frame
                            print "Error in %s on line %d" % (fname, lineno)
    • esta es una abominación
  28. -2

    Sólo estaba tratando de escribir algo más sencillo.

    #!/usr/bin/python
    
    from subprocess import Popen, PIPE
    import datetime
    import time 
    
    popen = Popen(["/bin/sleep", "10"]);
    pid = popen.pid
    sttime = time.time();
    waittime =  3
    
    print "Start time %s"%(sttime)
    
    while True:
        popen.poll();
        time.sleep(1)
        rcode = popen.returncode
        now = time.time();
        if [ rcode is None ]  and  [ now > (sttime + waittime) ] :
            print "Killing it now"
            popen.kill()
    • tiempo.sleep(1) es muy mala idea – imagine que usted desea ejecutar muchos comandos que duraría aproximadamente 0.002 seg. Usted debe esperar mientras poll() (consulte seleccionar, para Linux epol recomendado 🙂

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea