使用模块“子进程”; 有超时
以下是运行返回其 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
可能需要永远运行。
在 Windows 和 Linux 上运行的 Python 程序中实现超时的最简单方法是什么?
Here's the Python code to run an arbitrary command returning its stdout
data, or raise an exception on non-zero exit codes:
proc = subprocess.Popen(
cmd,
stderr=subprocess.STDOUT, # Merge stdout and stderr
stdout=subprocess.PIPE,
shell=True)
communicate
is used to wait for the process to exit:
stdoutdata, stderrdata = proc.communicate()
The subprocess
module does not support timeout--ability to kill a process running for more than X number of seconds--therefore, communicate
may take forever to run.
What is the simplest way to implement timeouts in a Python program meant to run on Windows and Linux?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
这是 Alex Martelli 的解决方案,作为具有适当进程终止的模块。 其他方法不起作用,因为它们不使用 proc.communicate()。 因此,如果您有一个产生大量输出的进程,它将填充其输出缓冲区,然后阻塞,直到您从中读取内容。
Here is Alex Martelli's solution as a module with proper process killing. The other approaches do not work because they do not use proc.communicate(). So if you have a process that produces lots of output, it will fill its output buffer and then block until you read something from it.
从Python 3.5开始,有一个新的
subprocess.run
通用命令(即替换check_call
、check_output
...),并且具有timeout=< /code> 参数也是如此。
它会引发
subprocess.TimeoutExpired
超时到期时出现异常。Since Python 3.5, there's a new
subprocess.run
universal command (that is meant to replacecheck_call
,check_output
...) and which has thetimeout=
parameter as well.It raises a
subprocess.TimeoutExpired
exception when the timeout expires.timeout
现在支持,如下: subprocess 模块中的 code>call() 和communicate()
(从 Python3.3 开始):这将调用命令并引发异常
如果命令在 20 后未完成, 秒。
然后,您可以处理异常以继续您的代码,例如:
希望这有帮助。
timeout
is now supported bycall()
andcommunicate()
in the subprocess module (as of Python3.3):This will call the command and raise the exception
if the command doesn't finish after 20 seconds.
You can then handle the exception to continue your code, something like:
Hope this helps.
令人惊讶的是没有人提到使用
timeout
timeout 5 ping -c 3 somehost
这显然不适用于每个用例,但如果您处理一个简单的脚本,这很难击败。
Mac 用户还可以通过
homebrew
在 coreutils 中以 gtimeout 形式使用。surprised nobody mentioned using
timeout
timeout 5 ping -c 3 somehost
This won't for work for every use case obviously, but if your dealing with a simple script, this is hard to beat.
Also available as gtimeout in coreutils via
homebrew
for mac users.我修改了 sussudio 答案。 现在函数返回:(
returncode
,stdout
,stderr
,timeout
) -stdout
并且stderr
被解码为 utf-8 字符串I've modified sussudio answer. Now function returns: (
returncode
,stdout
,stderr
,timeout
) -stdout
andstderr
is decoded to utf-8 string另一种选择是写入临时文件以防止 stdout 阻塞,而不需要使用 communications() 进行轮询。 这对我有用,而其他答案则不起作用; 例如在 Windows 上。
Another option is to write to a temporary file to prevent the stdout blocking instead of needing to poll with communicate(). This worked for me where the other answers did not; for example on windows.
预先添加 Linux 命令
timeout
并不是一个糟糕的解决方法,而且它对我有用。Prepending the Linux command
timeout
isn't a bad workaround and it worked for me.我将来自
jcollado
的线程解决方案添加到我的 Python 模块 easyprocess 。安装:
示例:
I added the solution with threading from
jcollado
to my Python module easyprocess.Install:
Example:
这是我的解决方案,我使用线程和事件:
实际操作:
Here is my solution, I was using Thread and Event:
In action:
我使用的解决方案是在 shell 命令前加上 timelimit 前缀。 如果命令花费的时间太长,timelimit 将停止它,并且 Popen 将有一个由 timelimit 设置的返回码。 如果是> 128,表示timelimit杀死了进程。
另请参阅具有超时和大输出(> 64K)的python子进程
The solution I use is to prefix the shell command with timelimit. If the comand takes too long, timelimit will stop it and Popen will have a returncode set by timelimit. If it is > 128, it means timelimit killed the process.
See also python subprocess with timeout and large output (>64K)
如果您使用的是 python 2,请尝试一下
if you are using python 2, give it a try
我已经实现了从其中一些中可以收集到的内容。 这适用于 Windows,并且由于这是一个社区 wiki,我想我也会分享我的代码:
然后从另一个类或文件:
I've implemented what I could gather from a few of these. This works in Windows, and since this is a community wiki, I figure I would share my code as well:
Then from another class or file:
一旦您了解了 *unix 中的完整进程运行机制,您将很容易找到更简单的解决方案:
考虑这个简单的示例,如何使用 select.select() 来实现可超时的 communications() 方法(现在 *nix 上几乎到处都可用)。 这也可以用 epoll/poll/kqueue 编写,但 select.select() 变体可能是一个很好的例子。 select.select() 的主要限制(速度和最大 1024 个 fd)不适用于您的任务。
这在 *nix 下工作,不创建线程,不使用信号,可以从任何线程(不仅仅是主线程)启动,并且足够快以从我的机器(i5 2.3ghz)上的 stdout 读取 250mb/s 的数据。
在通信结束时加入 stdout/stderr 时出现问题。 如果您有大量的程序输出,这可能会导致大量的内存使用。 但是您可以以较小的超时时间多次调用communicate()。
Once you understand full process running machinery in *unix, you will easily find simplier solution:
Consider this simple example how to make timeoutable communicate() meth using select.select() (available alsmost everythere on *nix nowadays). This also can be written with epoll/poll/kqueue, but select.select() variant could be a good example for you. And major limitations of select.select() (speed and 1024 max fds) are not applicapable for your task.
This works under *nix, does not create threads, does not uses signals, can be lauched from any thread (not only main), and fast enought to read 250mb/s of data from stdout on my machine (i5 2.3ghz).
There is a problem in join'ing stdout/stderr at the end of communicate. If you have huge program output this could lead to big memory usage. But you can call communicate() several times with smaller timeouts.
您可以使用
select
来完成此操作You can do this using
select
蟒蛇2.7
python 2.7
在 Python 3.7.8 中测试超时后捕获的输出示例:
异常 subprocess.TimeoutExpired 具有输出和其他成员:
更多信息: https://docs.python.org/3/library /subprocess.html#subprocess.TimeoutExpired
Example of captured output after timeout tested in Python 3.7.8:
The exception subprocess.TimeoutExpired has the output and other members:
More info: https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired
迟到的答案,仅适用于
Linux
,但如果有人想要使用subprocess.getstatusoutput()
,其中超时参数不可用,您可以使用 内置Linux超时位于命令开头,即:timeout参数:
--preserve-status
:保留退出状态--foreground
: 在前台运行25
:超时值(以秒为单位)Late answer and for
Linux
only, but in case someone wants to usesubprocess.getstatusoutput()
, where the timeout argument isn't available, you can use the built-in Linux timeout on the beginning of the command, i.e.:timeout
Arguments:--preserve-status
: Preserving the Exit Status--foreground
: Running in Foreground25
: timeout value in seconds我已经在 Windows、Linux 和 Mac 上成功使用了 killableprocess。 如果您使用 Cygwin Python,则需要 OSAF 版本的killableprocess< /a> 因为否则本机 Windows 进程不会被终止。
I've used killableprocess successfully on Windows, Linux and Mac. If you are using Cygwin Python, you'll need OSAF's version of killableprocess because otherwise native Windows processes won't get killed.
虽然我没有广泛地研究过它,但我发现了这个 装饰器 at ActiveState 似乎对于这类事情非常有用。 与 subprocess.Popen(..., close_fds=True) 一起,至少我已经准备好使用 Python 编写 shell 脚本了。
Although I haven't looked at it extensively, this decorator I found at ActiveState seems to be quite useful for this sort of thing. Along with
subprocess.Popen(..., close_fds=True)
, at least I'm ready for shell-scripting in Python.该解决方案在 shell=True 的情况下杀死进程树,将参数传递给进程(或不传递参数),有超时并获取回调的 stdout、stderr 和进程输出(它使用 psutil 作为kill_proc_tree)。 这是基于 SO 中发布的几个解决方案,包括 jcollado 的解决方案。 发布回应 Anson 和 jradice 在 jcollado 的回答中的评论。 在 Windows Srvr 2012 和 Ubuntu 14.04 中测试。 请注意,对于 Ubuntu,您需要将parent.children(...) 调用更改为parent.get_children(...)。
This solution kills the process tree in case of shell=True, passes parameters to the process (or not), has a timeout and gets the stdout, stderr and process output of the call back (it uses psutil for the kill_proc_tree). This was based on several solutions posted in SO including jcollado's. Posting in response to comments by Anson and jradice in jcollado's answer. Tested in Windows Srvr 2012 and Ubuntu 14.04. Please note that for Ubuntu you need to change the parent.children(...) call to parent.get_children(...).
有一个想法是对 Popen 类进行子类化并使用一些简单的方法装饰器对其进行扩展。 我们称之为 ExpirablePopen。
There's an idea to subclass the Popen class and extend it with some simple method decorators. Let's call it ExpirablePopen.
我遇到的问题是,如果多线程子进程花费的时间超过给定的超时长度,我想终止它。 我想在
Popen()
中设置超时,但没有成功。 然后,我意识到Popen().wait()
等于call()
,因此我想到在.wait( timeout=xxx)
方法,终于成功了。 因此,我这样解决了:I had the problem that I wanted to terminate a multithreading subprocess if it took longer than a given timeout length. I wanted to set a timeout in
Popen()
, but it did not work. Then, I realized thatPopen().wait()
is equal tocall()
and so I had the idea to set a timeout within the.wait(timeout=xxx)
method, which finally worked. Thus, I solved it this way:子进程 Popen.communicate 现在有一个
timeout
选项:您可以查看 文档。
Subprocess Popen.communicate now has a
timeout
option:You can take a look at the docs.
不幸的是,我的雇主对源代码的披露有非常严格的政策约束,所以我无法提供实际的代码。 但就我的口味而言,最好的解决方案是创建一个重写 Popen.wait() 的子类来轮询而不是无限期等待,并创建 Popen.__init__ 来接受超时参数。 完成此操作后,所有其他
Popen
方法(调用wait
)将按预期工作,包括communicate
。Unfortunately, I'm bound by very strict policies on the disclosure of source code by my employer, so I can't provide actual code. But for my taste the best solution is to create a subclass overriding
Popen.wait()
to poll instead of wait indefinitely, andPopen.__init__
to accept a timeout parameter. Once you do that, all the otherPopen
methods (which callwait
) will work as expected, includingcommunicate
.https://pypi.python.org/pypi/python-subprocess2 提供了对subprocess 模块允许您等待一段时间,否则终止。
因此,最多等待 10 秒让进程终止,否则杀死:
这与 windows 和 unix 兼容。 “results”是一个字典,它包含“returnCode”,它是应用程序的返回(如果必须被杀死,则为“None”),以及“actionTaken”。 如果进程正常完成,则为“SUBPROCESS2_PROCESS_COMPLETED”,或者根据所采取的操作,为“SUBPROCESS2_PROCESS_TERMINATED”和SUBPROCESS2_PROCESS_KILLED的掩码(有关完整详细信息,请参阅文档)
https://pypi.python.org/pypi/python-subprocess2 provides extensions to the subprocess module which allow you to wait up to a certain period of time, otherwise terminate.
So, to wait up to 10 seconds for the process to terminate, otherwise kill:
This is compatible with both windows and unix. "results" is a dictionary, it contains "returnCode" which is the return of the app (or None if it had to be killed), as well as "actionTaken". which will be "SUBPROCESS2_PROCESS_COMPLETED" if the process completed normally, or a mask of "SUBPROCESS2_PROCESS_TERMINATED" and SUBPROCESS2_PROCESS_KILLED depending on action taken (see documentation for full details)
对于 python 2.6+,使用 gevent
for python 2.6+, use gevent
在 Python 3.3+ 中:
output
是一个字节字符串,其中包含命令的合并 stdout、stderr 数据。check_output
引发CalledProcessError
与问题文本中指定的非零退出状态不同,与proc.communicate()
方法不同。我删除了
shell=True
因为它经常被不必要地使用。 如果cmd
确实需要它,您可以随时将其添加回来。 如果添加shell=True
即,如果子进程生成自己的后代;check_output()
的返回时间可能比超时指示的时间晚得多,请参阅子进程超时失败。Python 2.x 上通过
subprocess32
提供超时功能3.2+ 子进程模块的向后移植。In Python 3.3+:
output
is a byte string that contains command's merged stdout, stderr data.check_output
raisesCalledProcessError
on non-zero exit status as specified in the question's text unlikeproc.communicate()
method.I've removed
shell=True
because it is often used unnecessarily. You can always add it back ifcmd
indeed requires it. If you addshell=True
i.e., if the child process spawns its own descendants;check_output()
can return much later than the timeout indicates, see Subprocess timeout failure.The timeout feature is available on Python 2.x via the
subprocess32
backport of the 3.2+ subprocess module.我对底层细节了解不多; 但是,考虑到在
python 2.6 API 提供了等待线程和
终止进程,如何在单独的进程中运行该进程
线?
这段代码在我的机器上的输出是:
可以看出,在第一次执行时,进程
正确完成(返回代码 0),而在第二个中
进程被终止(返回代码-15)。
我没有在windows下测试过; 但是,除了更新示例之外
命令,我认为它应该有效,因为我没有在
记录任何表示 thread.join 或 process.terminate 的内容
不支持。
I don't know much about the low level details; but, given that in
python 2.6 the API offers the ability to wait for threads and
terminate processes, what about running the process in a separate
thread?
The output of this snippet in my machine is:
where it can be seen that, in the first execution, the process
finished correctly (return code 0), while the in the second one the
process was terminated (return code -15).
I haven't tested in windows; but, aside from updating the example
command, I think it should work since I haven't found in the
documentation anything that says that thread.join or process.terminate
is not supported.
jcollado的答案可以使用 threading.Timer 类进行简化:
jcollado's answer can be simplified using the threading.Timer class:
如果你使用的是 Unix,
If you're on Unix,