example - Python, Popen y seleccione-esperando a que finalice un proceso o un tiempo de espera excedido
subprocess.check_output example python (7)
Python 3.3
import subprocess as sp
try:
sp.check_call(["/subprocess"], timeout=10,
stdin=sp.DEVNULL, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
except sp.TimeoutError:
# timeout (the subprocess is killed at this point)
except sp.CalledProcessError:
# subprocess failed before timeout
else:
# subprocess ended successfully before timeout
Consulte TimeoutExpired docs .
Ejecuto un subproceso usando:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
Este subproceso podría salir inmediatamente con un error en stderr o seguir ejecutándose. Quiero detectar cualquiera de estas condiciones, la segunda esperando varios segundos.
Intenté esto:
SECONDS_TO_WAIT = 10
select.select([],
[p.stdout, p.stderr],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
pero solo regresa:
([],[],[])
en cualquier condición. ¿Que puedo hacer?
¿Has intentado utilizar el método Popen.Poll ()? Podrías hacer esto:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
time.sleep(SECONDS_TO_WAIT)
retcode = p.poll()
if retcode is not None:
# process has terminated
Esto hará que siempre espere 10 segundos, pero si el caso de falla es raro, esto se amortizaría en todos los casos de éxito.
Editar:
Qué tal si:
t_nought = time.time()
seconds_passed = 0
while(p.poll() is not None and seconds_passed < 10):
seconds_passed = time.time() - t_nought
if seconds_passed >= 10:
#TIMED OUT
Esto tiene la fealdad de ser una espera ocupada, pero creo que logra lo que quieres.
Además, mirando de nuevo la documentación de la llamada de selección, creo que es posible que desee cambiarla de la siguiente manera:
SECONDS_TO_WAIT = 10
select.select([p.stderr],
[],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
Como normalmente querría leer de stderr, quiere saber cuándo tiene algo disponible para leer (es decir, el caso de falla).
Espero que esto ayude.
Aquí hay un buen ejemplo:
from threading import Timer
from subprocess import Popen, PIPE
def kill_proc():
proc.kill()
proc = Popen("ping 127.0.0.1", shell=True)
t = Timer(60, kill_proc)
t.start()
proc.wait()
Esta es una paráfrasis de la respuesta de Evan, pero tiene en cuenta lo siguiente:
- Cancelación explícita del objeto del temporizador: si el intervalo del temporizador sería largo y el proceso saldrá por su propia voluntad, esto podría colgar el guión :(
Hay una carrera intrínseca en el enfoque del temporizador (el intento del temporizador acaba con el proceso justo después de que el proceso ha muerto y esto en Windows generará una excepción).
DEVNULL = open(os.devnull, "wb") process = Popen("c:/myExe.exe", stdout=DEVNULL) # no need for stdout def kill_process(): """ Kill process helper""" try: process.kill() except OSError: pass # Swallow the error timer = Timer(timeout_in_sec, kill_process) timer.start() process.wait() timer.cancel()
Esto es lo que se me ocurrió. Funciona cuando necesita y no necesita tiempo de espera en el proceso de p, pero con un ciclo semi-ocupado.
def runCmd(cmd, timeout=None):
''''''
Will execute a command, read the output and return it back.
@param cmd: command to execute
@param timeout: process timeout in seconds
@return: a tuple of three: first stdout, then stderr, then exit code
@raise OSError: on missing command or if a timeout was reached
''''''
ph_out = None # process output
ph_err = None # stderr
ph_ret = None # return code
p = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# if timeout is not set wait for process to complete
if not timeout:
ph_ret = p.wait()
else:
fin_time = time.time() + timeout
while p.poll() == None and fin_time > time.time():
time.sleep(1)
# if timeout reached, raise an exception
if fin_time < time.time():
# starting 2.6 subprocess has a kill() method which is preferable
# p.kill()
os.kill(p.pid, signal.SIGKILL)
raise OSError("Process timeout has been reached")
ph_ret = p.returncode
ph_out, ph_err = p.communicate()
return (ph_out, ph_err, ph_ret)
Si, como dijiste en los comentarios anteriores, solo estás retocando la salida cada vez y volviendo a ejecutar el comando, ¿algo así como el siguiente trabajo?
from threading import Timer
import subprocess
WAIT_TIME = 10.0
def check_cmd(cmd):
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def _check():
if p.poll()!=0:
print cmd+" did not quit within the given time period."
# check whether the given process has exited WAIT_TIME
# seconds from now
Timer(WAIT_TIME, _check).start()
check_cmd(''echo'')
check_cmd(''python'')
El código anterior, cuando se ejecuta, genera:
python did not quit within the given time period.
La única desventaja del código anterior que puedo pensar es en los procesos potencialmente superpuestos a medida que sigues corriendo check_cmd.
Usar seleccionar y dormir realmente no tiene mucho sentido. select (o cualquier mecanismo de sondeo de kernel) es intrínsecamente útil para la programación asincrónica, pero su ejemplo es síncrono. Entonces, reescriba su código para usar la moda de bloqueo normal o considere usar Twisted:
from twisted.internet.utils import getProcessOutputAndValue
from twisted.internet import reactor
def stop(r):
reactor.stop()
def eb(reason):
reason.printTraceback()
def cb(result):
stdout, stderr, exitcode = result
# do something
getProcessOutputAndValue(''/bin/someproc'', []
).addCallback(cb).addErrback(eb).addBoth(stop)
reactor.run()
Por cierto, hay una forma más segura de hacerlo con Twisted al escribir su propio ProcessProtocol:
http://twistedmatrix.com/projects/core/documentation/howto/process.html