我使用以下命令运行子进程:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
此子进程可以立即退出stderr上的错误,也可以继续运行。我想检测这些条件中的任何一个 - 后者等待几秒钟。
我试过了:
SECONDS_TO_WAIT = 10
select.select([],
[p.stdout, p.stderr],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
但它只会返回:
([],[],[])
在任何一种情况下。我该怎么办?
答案 0 :(得分:15)
您是否尝试过使用Popen.Poll()方法。你可以这样做:
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
这将使您始终等待10秒,但如果失败案例很少,则会在所有成功案例中摊销。
编辑:
怎么样:
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
这是一个忙碌的等待的丑陋,但我认为它完成了你想要的。
另外再次查看select call文档,我想你可能想要改变它如下:
SECONDS_TO_WAIT = 10
select.select([p.stderr],
[],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
由于您通常希望从stderr读取,因此您想知道何时可以读取内容(即失败案例)。
我希望这会有所帮助。
答案 1 :(得分:8)
这就是我想出的。在需要时工作,不需要在进程上超时,但是使用半繁忙的循环。
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)
答案 2 :(得分:2)
使用选择并且睡觉并没有多大意义。 select (或任何内核轮询机制)对于异步编程本身就很有用,但您的示例是同步的。因此要么重写代码以使用正常的阻塞方式,要么考虑使用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()
顺便提一下,使用Twisted编写自己的ProcessProtocol有一种更安全的方法:
http://twistedmatrix.com/projects/core/documentation/howto/process.html
答案 3 :(得分:2)
这是一个很好的例子:
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()
答案 4 :(得分:1)
正如你在上面的评论中所说的那样,你只是每次调整输出并重新运行命令,是否会像以下那样工作?
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')
上面的代码,运行时输出:
python did not quit within the given time period.
我能想到的上述代码的唯一缺点就是当你继续运行check_cmd时可能存在重叠的进程。
答案 5 :(得分:1)
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
答案 6 :(得分:0)
这是对Evan回答的解释,但它考虑了以下内容:
Timer方法存在一种固有的竞争(计时器尝试在进程死亡之后立即杀死进程,这将在Windows上引发异常)。
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()