我想执行一个进程,将执行时间限制为以秒为单位的超时,并获取该进程产生的输出。我想在windows,linux和freebsd上做这个。
我试着用三种不同的方式来实现这一点:
没有超时和输出捕获的subprocess.PIPE。
行为:按预期操作但不支持超时,我需要超时。。。
cmd_to-带超时和用于输出捕获的subprocess.PIPE。
行为:当输出>;=2^16字节时阻止子进程执行。
cmd_totf-带超时和tempfile.NamedTemporaryfile用于输出捕获。
行为:按预期操作,但使用磁盘上的临时文件。
下面提供这些以供进一步检查。
从下面的输出中可以看出,使用subprocessing.PIPE时,超时代码会阻止子进程的执行,并且子进程的输出为2^16字节。
子流程文档指出,调用process.wait()和使用subprocessing.PIPE时会出现这种情况,但是在使用process.poll()时不会发出警告,所以这里出了什么问题?
我在cmd_totf中有一个使用tempfile模块的解决方案,但是折衷的办法是它将输出写到磁盘,这是我真正想避免的。
所以我的问题是:
生成一组输出(“exp_gen.py”)的脚本:
#!/usr/bin/env python
import sys
output = "b"*int(sys.argv[1])
print output
子流程周围的包装器有三种不同的实现方式(cmd、cmd、cmd)
#!/usr/bin/env python
import subprocess, time, tempfile
bufsize = -1
def cmd(cmdline, timeout=60):
"""
Execute cmdline.
Uses subprocessing and subprocess.PIPE.
"""
p = subprocess.Popen(
cmdline,
bufsize = bufsize,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
out, err = p.communicate()
returncode = p.returncode
return (returncode, err, out)
def cmd_to(cmdline, timeout=60):
"""
Execute cmdline, limit execution time to 'timeout' seconds.
Uses subprocessing and subprocess.PIPE.
"""
p = subprocess.Popen(
cmdline,
bufsize = bufsize,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
t_begin = time.time() # Monitor execution time
seconds_passed = 0
while p.poll() is None and seconds_passed < timeout:
seconds_passed = time.time() - t_begin
time.sleep(0.1)
#if seconds_passed > timeout:
#
# try:
# p.stdout.close() # If they are not closed the fds will hang around until
# p.stderr.close() # os.fdlimit is exceeded and cause a nasty exception
# p.terminate() # Important to close the fds prior to terminating the process!
# # NOTE: Are there any other "non-freed" resources?
# except:
# pass
#
# raise TimeoutInterrupt
out, err = p.communicate()
returncode = p.returncode
return (returncode, err, out)
def cmd_totf(cmdline, timeout=60):
"""
Execute cmdline, limit execution time to 'timeout' seconds.
Uses subprocessing and tempfile instead of subprocessing.PIPE.
"""
output = tempfile.NamedTemporaryFile(delete=False)
error = tempfile.NamedTemporaryFile(delete=False)
p = subprocess.Popen(
cmdline,
bufsize = 0,
shell = False,
stdin = None,
stdout = output,
stderr = error
)
t_begin = time.time() # Monitor execution time
seconds_passed = 0
while p.poll() is None and seconds_passed < timeout:
seconds_passed = time.time() - t_begin
time.sleep(0.1)
#if seconds_passed > timeout:
#
# try:
# p.stdout.close() # If they are not closed the fds will hang around until
# p.stderr.close() # os.fdlimit is exceeded and cause a nasty exception
# p.terminate() # Important to close the fds prior to terminating the process!
# # NOTE: Are there any other "non-freed" resources?
# except:
# pass
#
# raise TimeoutInterrupt
p.wait()
returncode = p.returncode
fd = open(output.name)
out = fd.read()
fd.close()
fd = open(error.name)
err = fd.read()
fd.close()
error.close()
output.close()
return (returncode, err, out)
if __name__ == "__main__":
implementations = [cmd, cmd_to, cmd_totf]
bytes = ['65535', '65536', str(1024*1024)]
timeouts = [5]
for timeout in timeouts:
for size in bytes:
for i in implementations:
t_begin = time.time()
seconds_passed = 0
rc, err, output = i(['exp_gen.py', size], timeout)
seconds_passed = time.time() - t_begin
filler = ' '*(8-len(i.func_name))
print "[%s%s: timeout=%d, iosize=%s, seconds=%f]" % (repr(i.func_name), filler, timeout, size, seconds_passed)
执行输出:
['cmd' : timeout=5, iosize=65535, seconds=0.016447]
['cmd_to' : timeout=5, iosize=65535, seconds=0.103022]
['cmd_totf': timeout=5, iosize=65535, seconds=0.107176]
['cmd' : timeout=5, iosize=65536, seconds=0.028105]
['cmd_to' : timeout=5, iosize=65536, seconds=5.116658]
['cmd_totf': timeout=5, iosize=65536, seconds=0.104905]
['cmd' : timeout=5, iosize=1048576, seconds=0.025964]
['cmd_to' : timeout=5, iosize=1048576, seconds=5.128062]
['cmd_totf': timeout=5, iosize=1048576, seconds=0.103183]
免责声明:此答案不在windows上测试,也不在freebsd上测试。但是使用的模块应该可以在这些系统上工作。我相信这应该是你问题的有效答案-对我有用。
这是我在linux上破解的代码。这是几个Stackoverflow线程和我自己在Python 3文档中的研究的结合。
本规范的主要特点:
唯一的代码依赖项是enum as implementedhere,但是可以很容易地将代码更改为不使用。它只用于区分两个超时-如果您愿意,请使用单独的异常。
以下是我们非常感谢的代码反馈: (2012年6月29日编辑-代码现在实际工作)
用于:
第一个参数应该是命令及其参数的列表。它用于
Popen(shell=False)
调用,其超时以秒为单位。当前没有禁用超时的代码。将timeout_no_output
设置为time_runtime
可有效禁用可重触发timeout_no_output
。stdin_string
可以是要发送到命令的标准输入的任何字符串。如果命令不需要任何输入,则设置为None
。如果提供了字符串,则会附加最后一个“\n”。与子流程文档中的所有警告相反,直接读取process.stdout和process.stderr提供了更好的解决方案。
我的意思是,我可以读取超过2^16字节的进程的输出,而不必临时将输出存储在磁盘上。
代码如下:
相关问题 更多 >
编程相关推荐