下面是运行任意命令返回其标准输出数据的Python代码,或在非零退出码上引发异常:

proc = subprocess.Popen(
    cmd,
    stderr=subprocess.STDOUT,  # Merge stdout and stderr
    stdout=subprocess.PIPE,
    shell=True)

communication用于等待进程退出:

stdoutdata, stderrdata = proc.communicate()

子进程模块不支持超时——杀死运行超过X秒的进程的能力——因此,通信可能需要很长时间才能运行。

在Windows和Linux上运行的Python程序中实现超时的最简单方法是什么?


当前回答

我已经在Windows, Linux和Mac上成功地使用了killableprocess。如果你使用Cygwin Python,你将需要OSAF的killableprocess版本,否则本机Windows进程将不会被杀死。

其他回答

不幸的是,我的雇主在公开源代码方面有非常严格的规定,所以我不能提供实际的代码。但对我来说,最好的解决方案是创建一个子类重写Popen.wait()来轮询,而不是无限期地等待。__init__接受超时参数。一旦你这样做,所有其他Popen方法(调用等待)将按预期工作,包括通信。

timeout现在由子进程模块中的call()和communication()支持(从Python3.3开始):

import subprocess

subprocess.call("command", timeout=20, shell=True)

这将调用该命令并引发异常

subprocess.TimeoutExpired

如果命令在20秒后还没有完成。

然后你可以处理异常来继续你的代码,就像这样:

try:
    subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
    # insert code here

希望这能有所帮助。

我对底层细节了解不多;但是,考虑到这一点 python 2.6的API提供了等待线程和的能力 终止进程,那么在一个单独的进程中运行该进程呢 的线程吗?

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

这段代码在我的机器中的输出是:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

在哪里可以看到,在第一次执行的过程 正确完成(返回代码0),而在第二个 进程被终止(返回代码-15)。

我没有在windows中测试;但是,除了更新示例之外 命令,我想它应该工作,因为我还没有找到 记录任何说明该线程的内容。Join或process.terminate 不支持。

另一种选择是写入临时文件以防止标准输出阻塞,而不需要使用communication()轮询。在其他答案没有的地方,这个方法对我有用;比如在窗户上。

    outFile =  tempfile.SpooledTemporaryFile() 
    errFile =   tempfile.SpooledTemporaryFile() 
    proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
    wait_remaining_sec = timeout

    while proc.poll() is None and wait_remaining_sec > 0:
        time.sleep(1)
        wait_remaining_sec -= 1

    if wait_remaining_sec <= 0:
        killProc(proc.pid)
        raise ProcessIncompleteError(proc, timeout)

    # read temp streams from start
    outFile.seek(0);
    errFile.seek(0);
    out = outFile.read()
    err = errFile.read()
    outFile.close()
    errFile.close()

我添加了从jcollado线程到我的Python模块easyprocess的解决方案。

安装:

pip install easyprocess

例子:

from easyprocess import Proc

# shell is not supported!
stdout=Proc('ping localhost').call(timeout=1.5).stdout
print stdout