这是运行任意命令返回其 stdout
数据的Python代码,或者在非零退出代码上引发异常:
proc = subprocess.Popen(
cmd,
stderr=subprocess.STDOUT, # Merge stdout and stderr
stdout=subprocess.PIPE,
shell=True)
communicate
用于等待进程退出:
stdoutdata, stderrdata = proc.communicate()
subprocess
模块不支持超时 - 能够终止运行超过X秒的进程 - 因此, communicate
可能需要永久运行 .
什么是在Python程序中实现超时的 simplest 方式,以便在Windows和Linux上运行?
29 回答
如果你在Unix上,
在Python 3.3中:
output
是一个字节字符串,包含命令的合并stdout,stderr数据 .与
proc.communicate()
方法不同,此代码在问题文本中指定的非零退出状态上引发CalledProcessError
.我删除了
shell=True
因为它经常被不必要地使用 . 如果cmd
确实需要它,您可以随时添加它 . 如果添加shell=True
,即子进程生成自己的后代;check_output()
可以在超时指示的时间之后返回,请参阅Subprocess timeout failure .Python 2.x上的超时功能可通过3.2子进程模块的subprocess32 backport获得 .
我对低级细节知之甚少;但是,鉴于在python 2.6中,API提供了等待线程和终止进程的能力,那么在单独的线程中运行进程呢?
我机器中此代码段的输出是:
可以看出,在第一次执行中,进程正确完成(返回代码0),而在第二次执行中进程终止(返回代码-15) .
我没有在Windows中测试过;但是,除了更新示例命令之外,我认为它应该可以工作,因为我没有在文档中找到任何说明不支持thread.join或process.terminate的内容 .
使用threading.Timer类可以简化jcollado的答案:
Alex Martelli的解决方案是一个具有适当进程终止的模块 . 其他方法不起作用,因为它们不使用proc.communicate() . 因此,如果您有一个产生大量输出的进程,它将填充其输出缓冲区然后阻塞,直到您从中读取内容 .
我修改了 sussudio 回答 . 现在函数返回:(
returncode
,stdout
,stderr
,timeout
) -stdout
和stderr
被解码为utf-8字符串惊讶没有提到使用
timeout
timeout 5 ping -c 3 somehost
这显然不适用于每个用例,但如果你处理一个简单的脚本,这很难被击败 .
对于mac用户,也可以通过
homebrew
在coreutils中作为gtimeout使用 .另一种选择是写入临时文件以防止stdout阻塞而不需要使用communic()进行轮询 . 这对我有用,而其他答案没有;例如在Windows上 .
timeout is now supported由
call()
和communicate()
在子进程模块中(从Python3.3开始):这将调用该命令并引发异常
如果命令在20秒后没有完成 .
然后,您可以处理异常以继续您的代码,例如:
希望这可以帮助 .
这是我的解决方案,我使用线程和事件:
在行动:
我使用的解决方案是在shell命令前加上timelimit . 如果命令花费太长时间,则timelimit将停止它并且Popen将具有由timelimit设置的返回码 . 如果它> 128,则意味着timelimit杀死了该进程 .
另见python subprocess with timeout and large output (>64K)
我添加了从
jcollado
到我的Python模块easyprocess的线程解决方案 .安装:
例:
如果您使用的是python 2,请尝试一下
只是想写一些更简单的东西 .
我没有提及但是从Python 3.5开始,有一个新的subprocess.run通用命令(意味着要取代
check_call
,check_output
...),并且它也有timeout
参数 .超时到期时,它会引发
subprocess.TimeoutExpired
异常 .我已经实现了从其中一些我可以收集到的东西 . 这适用于Windows,因为这是一个社区维基,我想我也会分享我的代码:
然后从另一个类或文件:
一旦您了解* unix中的完整流程运行机器,您将很容易找到更简单的解决方案:
考虑这个简单的例子如何使用select.select()进行超时的communic()方法(现在在* nix上几乎可以使用) . 这也可以用epoll / poll / kqueue编写,但select.select()变体可能是一个很好的例子 . select.select()(速度和1024最大fds)的主要限制不适用于您的任务 .
这在* nix下工作,不创建线程,不使用信号,可以从任何线程(不仅是主线程)推出,并且快速从我的机器上的stdout读取250mb / s的数据(i5 2.3ghz) .
在通信结束时加入stdout / stderr会出现问题 . 如果你有巨大的程序输出,这可能会导致大量的内存使用 . 但是你可以用较小的超时调用communic()几次 .
你可以用
select
做到这一点预装Linux命令
timeout
并不是一个糟糕的解决方法,它对我有用 .我在Windows,Linux和Mac上成功使用了killableprocess . 如果您使用Cygwin Python,则需要OSAF's version of killableprocess,否则本机Windows进程将不会被杀死 .
虽然我没有广泛地看过它,但我在ActiveState中发现这似乎对这类事情非常有用 . 与
subprocess.Popen(..., close_fds=True)
一起,至少我已经准备好在Python中使用shell脚本了 .有一个想法是继承Popen类并使用一些简单的方法装饰器扩展它 . 我们称之为ExpirablePopen .
我遇到的问题是,如果花费超过给定的超时长度,我想终止多线程子进程 . 我想在
Popen()
中设置超时,但它不起作用 . 然后,我意识到Popen().wait()
等于call()
,所以我有想法在.wait(timeout=xxx)
方法中设置超时,最终有效 . 因此,我这样解决了:不幸的是,我'm bound by very strict policies on the disclosure of source code by my employer, so I can' t提供了实际的代码 . 但根据我的口味,最好的解决方案是创建一个子类重写
Popen.wait()
以轮询而不是无限期地等待,并且Popen.__init__
接受超时参数 . 一旦你这样做,所有其他Popen
方法(调用wait
)将按预期工作,包括communicate
.https://pypi.python.org/pypi/python-subprocess2提供了子进程模块的扩展,允许您等待一段时间,否则终止 .
所以,等待最多10秒钟的进程终止,否则杀死:
这与windows和unix兼容 . “results”是一个字典,它包含“returnCode”,它是应用程序的返回(如果必须被杀死则为None),以及“actionTaken” . 如果进程正常完成,则为“SUBPROCESS2_PROCESS_COMPLETED”,或者根据所采取的操作设置掩码“SUBPROCESS2_PROCESS_TERMINATED”和SUBPROCESS2_PROCESS_KILLED(有关详细信息,请参阅文档)
如果shell = True,该解决方案会终止进程树,将参数传递给进程(或不传递),具有超时并获取回调的stdout,stderr和process输出(它使用psutil作为kill_proc_tree) . 这是基于SO中发布的几个解决方案,包括jcollado的 . 发表回应Anson和jradice在jcollado答案中的评论 . 在Windows Srvr 2012和Ubuntu 14.04中测试过 . 请注意,对于Ubuntu,您需要将parent.children(...)调用更改为parent.get_children(...) .
对于python 2.6,使用gevent
python 2.7