【问题标题】:Run executable from python with a timeout超时从python运行可执行文件
【发布时间】:2017-10-08 22:11:15
【问题描述】:

我是一个很长一段时间的读者,但这是我在这里的第一个问题。
我正在编写一个 Python 3 程序(我目前正在自学)来编译和运行一组应该做同样事情的 C++ 程序(学生提交的作业)并将它们的输出与预期的结果进行比较输出我已经在一个文本文件中。

我正在寻找一种方法来运行这些可执行文件,以使我能够:

  • 如果可执行文件没有结束,则在某个时间限制(例如 10 秒)后终止它。
  • 捕获可执行文件的所有输出(包括 stdout、stderr 和任何“shell”消息,如分段错误、内存/核心转储等)
  • 即使可执行文件崩溃或被强制终止(超过允许的时间后),我也需要捕获到该点的“部分”输出。
  • 屏幕上没有任何内容(没有输出或错误消息)。

到目前为止,我尝试过的所有事情(从类似问题的答案)都缺少一些东西。我找不到满足我所有需求的产品(以上几点)。 以下是一些示例:

  • check_output()(如在this 问题中)在可执行文件崩溃时不会收集输出,并且某些消息(如核心转储)仍会显示在屏幕上。 p>

  • Popen()(如this)似乎不会终止可执行文件(超过时间时)。 python 线程结束(我希望我在这里使用正确的术语),但可执行文件继续运行。

  • 尽管不鼓励使用像 os.system() 这样的方法,但我还是尝试了它们,但它们并没有表现得更好。

如果有人能指出实现这一目标的方法,我将不胜感激。我为任何语法错误道歉,因为英语不是我的第一语言。如果需要任何进一步的细节,请告诉我。 谢谢,

编辑:

这是我的 python 代码的最小副本:

from subprocess import STDOUT, check_output, TimeoutExpired

timeLimit = 3  # seconds
strOutput = ''

try:
    output = check_output('./a.out', stderr = STDOUT, timeout = timeLimit)
    strOutput = ''.join(map(chr, output))

except TimeoutExpired as e:
    strOutput += 'Error: Time limit exceeded, terminated..'

except Exception as e:
    strOutput += 'Error: ' + str(e)

f = open('report.txt','w')
f.write(strOutput)
f.close()

我还包括一些 C++ 示例,其中包含一些错误,以创建用于测试先前代码的可执行文件。 它们都可以编译为:g++ programName.cpp

#include <iostream>
using namespace std;

int main()
{
   cout << "This one is working correctly!" << endl;
}

以下代码会导致“除以零”错误。 check_output() 未捕获此错误之前打印的语句

#include <iostream>
using namespace std;

int main()
{
   cout << "Trying to divide by zero.." << endl;
   cout << 3 / 0 << endl;
}

此代码等待意外输入(应在指定超时后由脚本终止)

#include <iostream>
using namespace std;

int main()
{
   int x;
   cout << "Waiting for an unexpected input.." << endl;
   cin >> x;
}

我找不到重现 核心转储 问题的可靠方法。核心转储是一个特殊情况问题,因为 check_output() 无法捕获其输出(它直接/仅进入屏幕)。

附:我无法添加标签“check_output()”,我没有足够的声誉。

【问题讨论】:

  • 你使用的是什么操作系统?
  • 我目前在我的 Linux Mint 18.1 笔记本电脑上运行,但该脚本旨在在学校的 Ubuntu 16.04 服务器上运行。
  • subprocess.call()timeout 在其异常处理程序中调用 p.kill()p.wait(),如果超时到期,则会调用该处理程序。因此,它确实实际上杀死了立即生成的进程。
  • 现在,如果你生成的进程有孩子,杀死它可能不会杀死孩子——但如果这是你的问题,你应该提供一个 minimal reproducible example 让其他人复制它,作为最佳实践修复方法会因细节而异。在某些情况下,正确的解决方法可能是修改调用它的方式以展平进程树,从而完全避免拥有这些子进程。 (如果你有一个中间的 shell 脚本,拥有它exec 它产生的程序属于此类解决方案)。
  • 感谢大家的回复。我编辑了问题以包含所需的代码部分。

标签: python popen os.system


【解决方案1】:

一个简单的解决方案可能是使用您目前找到的所有解决方案,但不要运行程序一次,而是多次运行它们。

一次,确定运行时间, 另一次,确定输出 等等 这样,您还可以测试是否给定相同的输入,它们是否提供相同的输出。 它可能不是您正在寻找的“超级优雅”的解决方案,但现在它可能就足够了,直到您在 python 中找到子进程的圣杯。

此外,您可以将输出重定向到文件以获得部分结果+超时,然后读取文件以获取部分结果。

【讨论】:

  • 感谢您的回答。运行时间实际上不是问题。我使用它只是为了避免具有无限循环的程序。我的主要问题是捕获 all 输出。我找不到使用 check_output() 将输出重定向到文件的方法,所以如果您能指出一个工作示例(我对 python 比较陌生),我将不胜感激。
  • 这是我正在尝试的(重定向到文件)output = check_output(['./a.out', ' &gt;report.txt'], stderr = STDOUT, timeout = timeLimit)
  • 除了编译程序之外,我还对编译器的输出进行了一些基本分析(检查某些警告,计算特定错误等)。我也不希望输出与我已经拥有的输出 exact 匹配(因此使用类似 diff 的东西不是一种选择)。我决定使用像 python 这样的“高级”语言,因为我不知道如何使用 bash 来完成所有这些操作。
  • 也许问题可能在于尝试对带有错误和警告的程序进行评分。如果它没有正确终止,或者它没有做它应该做的事情,那有什么意义呢?行业客户会接受半成品软件吗?对我来说,他们要么跑,要么不做。
【解决方案2】:

基于@CharlesDuffy cmets,我简化了进程树以使其更扁平,并切换到subprocess.call(),它具有捕获“部分”输出的优势(在可执行文件崩溃之前)。
核心转储 但是,subprocess.call() 仍然没有捕捉到,它进入了屏幕。
有什么方法可以捕获核心转储的输出吗?或者至少防止它出现在屏幕上?

from subprocess import call, TimeoutExpired

timeLimit = 3  # seconds
errMsg = ''
ret_code = 0
f = open('report.txt','w')

try:
    ret_code = call('./a.out', stdout = f, stderr = f, timeout = timeLimit)

except TimeoutExpired as e:
    errMsg = 'Error: Time limit exceeded, terminating..'

except Exception as e:
    errMsg = 'Error: ' + str(e)

if ret_code:
    errMsg = 'Error: ' + str(ret_code)

f.write(errMsg)
f.close()

【讨论】:

    猜你喜欢
    • 2021-12-12
    • 1970-01-01
    • 2016-12-20
    • 2014-06-03
    • 2013-03-12
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多