下面是运行任意命令返回其标准输出数据的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程序中实现超时的最简单方法是什么?


当前回答

在Python 3.7.8中超时测试后捕获的输出示例:

try:
    return subprocess.run(command, shell=True, capture_output=True, timeout=20, cwd=cwd, universal_newlines=True)
except subprocess.TimeoutExpired as e:
    print(e.output.decode(encoding="utf-8", errors="ignore"))
    assert False;

异常子进程。TimeoutExpired有输出和其他成员:

cmd -用于生成子进程的命令。 timeout -超时时间,单位为秒。 output -子进程的输出,如果它是由run()或 check_output()。否则,没有。 stdout -输出的别名,用于与stderr对称。 stderr -子进程的stderr输出,如果它被捕获 run()。否则,没有。

更多信息:https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired

其他回答

仅适用于Linux,但如果有人想使用subprocess.getstatusoutput(),其中timeout参数不可用,您可以在命令的开头使用内置的Linux timeout,即:

import subprocess

timeout = 25 # seconds
cmd = f"timeout --preserve-status --foreground {timeout} ping duckgo.com"
exit_c, out = subprocess.getstatusoutput(cmd)

if (exit_c == 0):
    print("success")
else:
    print("Error: ", out)

超时参数:

——preserve-status:保留退出状态 ——前台:在前台运行 25:超时时间,单位为秒

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

https://pypi.python.org/pypi/python-subprocess2提供了子流程模块的扩展,允许您等待一段时间,否则将终止。

因此,等待最多10秒的进程终止,否则kill:

pipe  = subprocess.Popen('...')

timeout =  10

results = pipe.waitOrTerminate(timeout)

这与windows和unix兼容。"results"是一个字典,它包含"returnCode",这是应用程序的返回(或None,如果它必须被杀死),以及"actionTaken"。如果流程正常完成,则为“SUBPROCESS2_PROCESS_COMPLETED”,或者根据所采取的操作,为“SUBPROCESS2_PROCESS_TERMINATED”和SUBPROCESS2_PROCESS_KILLED(详细信息请参阅文档)

没想到居然没人提到超时

Timeout 5 ping -c 3 somehost

显然,这并不适用于每个用例,但如果您处理的是一个简单的脚本,那么这是很难克服的。

mac用户也可以通过homebrew在coreutils中使用gtimeout。

另一种选择是写入临时文件以防止标准输出阻塞,而不需要使用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()