Je lance un sous-processus en utilisant:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
Ce sous-processus peut soit se terminer immédiatement avec une erreur sur stderr, soit continuer à fonctionner. Je veux détecter l'une ou l'autre de ces conditions - cette dernière en attendant plusieurs secondes.
J'ai essayé ceci:
SECONDS_TO_WAIT = 10
select.select([],
[p.stdout, p.stderr],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
mais ça revient juste:
([],[],[])
à chaque condition. Que puis-je faire?
Avez-vous essayé d'utiliser la méthode Popen.Poll (). Vous pouvez simplement faire ceci:
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
Cela vous obligera à toujours attendre 10 secondes, mais si le cas d'échec est rare, il sera amorti sur tous les cas de réussite.
Modifier:
Que diriez-vous:
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
Cela a la laideur d'être une attente occupée, mais je pense que cela accomplit ce que vous voulez.
De plus, en regardant de nouveau la documentation sur les appels sélectifs, je pense que vous voudrez peut-être la modifier comme suit:
SECONDS_TO_WAIT = 10
select.select([p.stderr],
[],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
Puisque vous voudriez généralement lire à partir de stderr, vous voulez savoir quand il y a quelque chose à lire (par exemple, le cas d'échec).
J'espère que ça aide.
C'est ce que je suis venu avec. Fonctionne lorsque vous avez besoin et que vous n'avez pas besoin de délai d'expiration sur le processus thep, mais avec une boucle semi-occupée.
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)
Voici un bel exemple:
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()
Utiliser select et dormir n'a pas vraiment de sens. select (ou tout mécanisme de scrutation du noyau) est intrinsèquement utile pour la programmation asynchrone, mais votre exemple est synchrone. Donc, réécrivez votre code pour utiliser le mode de blocage normal ou envisagez d’utiliser 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()
À propos, il existe un moyen plus sûr de le faire avec Twisted en écrivant votre propre ProcessProtocol:
http://twistedmatrix.com/projects/core/documentation/howto/process.html
Si, comme vous l'avez dit dans les commentaires ci-dessus, vous modifiez simplement la sortie à chaque fois et réexécutez la commande, le travail suivant pourrait-il fonctionner?
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')
Le code ci-dessus, lorsqu'il est exécuté, affiche:
python did not quit within the given time period.
Le seul inconvénient du code ci-dessus auquel je puisse penser est le risque de chevauchement des processus au fur et à mesure de l'exécution de check_cmd.
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
Voir Documents expirés Timeout .
Ceci est une paraphrase de la réponse d'Evan, mais il prend en compte les éléments suivants:
Il y a une course intrinsèque dans l'approche du minuteur (la tentative du minuteur tuant le processus juste après le processus est mort et ceci sur Windows lève une exception).
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()