前言
hello大家好,本学期的javaWeb实习做了个OJ网站,ui参考PTA,详细介绍参考博主后面的博客,本篇文章主要介绍后台bat脚本语言实现的判题系统部分…
版本
v1.0
注:为啥要有版本呢???嘻嘻。。。
目前系统只实现了了C++判题,java其他语言的懒得弄了,然后系统还有一些bug未修复,总之就是未完善,欢迎各位大佬指导指导…
模块说明
-
截图
-
说明
文件名 | 描述 | 备注 |
---|---|---|
judgeMain.bat | 判题脚本入口程序需传入相应参数 | |
compile.bat | 用于编译的bat脚本 | |
runJudge.bat | 运行程序并判题的脚本 | |
timeOverExit.bat | 程序超时的自动终止脚本 | |
getUseMemory.bat | 获取程序内存消耗的脚本 | |
updateDatabase.bat | 更新数据库判题状态的脚本 |
注:
judgeMain.bat runJudge.bat timeOverExit.bat getUserMemory.bat配备相应的vbs启动文件以实现后台隐藏运行,详细代码请看下文GitHub链接
核心模块代码
Talk is cheap. Show me the code. ------Linus Torvalds
judgeMain.bat
rem @echo off
rem 注意文件名不要和系统命令一样如main
rem 注意注释使用'rem'
rem 判题入口文件
rem 需编译的文件
rem 提交的代码ID
set submitProblemId=%1
rem 需编译的文件
set compileFilePath=%2
rem 可执行文件
set exeFilePath=%3
rem 运行时的进程名
set processName=%4
rem 编译错误输出文件
set compileErrFilePath=%5
rem 判题输入数据文件
set inputFile=%6
rem 用户运行的输出
set outputFile=%7
rem 临时输出文件
set tempOutputFile=%8
rem 判题文件
set judgeFile=%9
rem 超过十个参数则右移
rem 判题结果数据文件
shift /1
set resultFile=%9
rem 时间限制
shift /2
set /a timeLimit=%9
rem 内存限制
shift /3
set /a memoryLimit=%9
rem 判题状态
rem -1 等待 0 通过 1编译错误 2答案错误 3运行超时 4内存超限
set /a resultStatus=1
rem 超时强制关闭时间 8s
set /a exitTime=8
rem 运行时间
set /a executeTime=0
rem 使用内存
set /a executeMemory=0
:init
rem 判断参数错误,所有参数不能为空
set errArg=无
if "%submitProblemId%" equ "" goto errEnd
if "%compileFilePath%" equ "" goto errEnd
if "%exeFilePath%" equ "" goto errEnd
if "%processName%" equ "" goto errEnd
if "%compileErrFilePath%" equ "" goto errEnd
if "%inputFile%" equ "" goto errEnd
if "%outputFile%" equ "" goto errEnd
if "%tempOutputFile%" equ "" goto errEnd
if "%judgeFile%" equ "" goto errEnd
if "%resultFile%" equ "" goto errEnd
if "%timeLimit%" equ "" goto errEnd
if "%memoryLimit%" equ "" goto errEnd
rem 初始化
cd .>%resultFile%
:compile
rem 编译结果
for /f %%i in ('compile.bat %compileFilePath% %exeFilePath% %compileErrFilePath%') do (
set /a resultStatus=%%i
)
if "%resultStatus%" EQU "2" (
rem 编译成功
) else (
echo %date% %time:~0,8%>>debug.txt
echo 编译失败%compileFilePath%>>debug.txt
goto database
)
:timeOverExit
rem 超时则强制退出
wscript timeOverExit.vbs %processName% %resultFile% %exitTime%
:getUseMemory
rem 获取内存消耗 程序
wscript getUseMemory.vbs %processName% %resultFile%
:runJudge
wscript runJudge.vbs %exeFilePath% %inputFile% %outputFile% %judgeFile% %resultFile% %tempOutputFile% %timeLimit%
:loopReadResult
rem 循环读取结果
rem 注意变量延迟问题
for /f "tokens=1,2 delims=:" %%i in (%resultFile%) do (
if "%%i" equ "executeTime" (
set /a executeTime=%%j
)
if "%%i" equ "executeMemory" (
set /a executeMemory=%%j
)
if "%%i" equ "nowStatus" (
set /a resultStatus=%%j
goto endLoop
)
)
rem 每次等待1s
choice /t 1 /d y /n >nul
goto loopReadResult
:endLoop
if %executeMemory% GEQ %memoryLimit% (
set /a resultStatus=4
)
:database
rem 此处为数据库更新的操作
rem 数据定义
rem 注意字符串加双引号 ""
rem 注意不要重复操作>>txt
call updateDatabase.bat %submitProblemId% %resultStatus% %compileErrFilePath% %executeTime% %executeMemory% >>dataBaseErr.txt 2>>&1
echo %date% %time:~0,8%>>log.txt
echo 题目评测完成,ID:%submitProblemId%>>log.txt
echo 题目ID:%submitProblemId%,评测结果:%resultStatus%,用时:%executeTime%,消耗内存:%executeMemory% >>log.txt
goto end
:errEnd
echo %date% %time:~0,8%>>debug.txt
echo 错误参数%errArg%>>debug.txt
echo 参数错误
:end
echo 评测程序结束
echo 执行究极自身终止程序
rem taskkill /im cmd.exe /f
exit
compile.bat
@echo off
rem 需编译的文件
set compileFilePath=%1
rem 可执行文件
set exeFilePath=%2
rem 编译错误输出文件
set compileErrFilePath=%3
rem 编译指令
set compileCMD=g++ %compileFilePath% -o %exeFilePath%
rem 编译结果 0 成功 1失败
set /a result=1
:compile
rem 注意括号前后要有空格
if exist %compileFilePath% (
rem 编译并输出错误,先清空错误输出
cd .>%compileErrFilePath%
%compileCMD%>%compileErrFilePath% 2>&1
goto checkCompile
)
rem 编译文件不存在则编译失败
goto end
:checkCompile
for %%a in ("%compileErrFilePath%") do (
if "%%~za" equ "0" (
rem 编译无误
set /a result=2
) else (
set /a result=1
)
)
:end
echo %result%
exit
runJudge.bat
@echo off
setlocal enabledelayedexpansion
rem 可执行文件
set exeFilePath=%1
rem 判题输入数据文件
set inputFile=%2
rem 用户运行的输出
set outputFile=%3
rem 判题文件
set judgeFile=%4
rem 判题结果数据文件
set resultFile=%5
rem 临时输出文件
set tempOutputFile=%6
rem 时间限制(平均)
set /a limitTime=%7
:runExe
rem 清空output文件
cd . >%outputFile%
rem 必须添加delims不然会以空格作为分隔
rem 每个测试用例以$符号结尾
set num = 0
rem 计算平均耗时
set /a avaTime=0
rem 获得耗时单位ms
rem 秒 0~9
set /a sTime1=%time:~7,1%
rem 100ms 0~9
set /a sTime2=%time:~9,1%
rem 10ms 0~9
set /a sTime3=%time:~10,1%
rem 开始时间单位ms
set /a sTime=%sTime1%*1000+%sTime2%*100+%sTime3%*10
rem 清空temp
cd .>%tempOutputFile%
for /f "delims=" %%i in (%inputFile%) do (
if not '%%i' equ '$' (
echo %%i>>%tempOutputFile%
) else (
call %exeFilePath% <%tempOutputFile% >>%outputFile%
set /a num+=1
rem 清空temp
cd .>%tempOutputFile%
)
)
:getTime
rem 获得耗时单位ms
rem 秒 0~9
set /a eTime1=%time:~7,1%
rem 100ms 0~9
set /a eTime2=%time:~9,1%
rem 10ms 0~9
set /a eTime3=%time:~10,1%
rem 结束时间单位ms
set /a eTime=%eTime1%*1000+%eTime2%*100+%eTime3%*10
rem 获得耗时
rem 注意时间循环
set costTime=0
if %eTime% GEQ %sTime% (
set /a costTime=%eTime%-%sTime%
) else (
set /a costTime=%eTime%-%sTime%+10000
)
rem 计算平均耗时
set /a avaTime=%costTime%/%num%
:chekIsErrExit
rem 检查是否被异常终止
rem 检查是否超时
rem 阻碍程序运行不输出
for /f "tokens=1,2 delims=:" %%a in (%resultFile%) do (
if "%%a" equ "nowStatus" (
rem 程序已超时被timeOverExit终止
if "%%b" equ "3" (
set /a resultStatus=3
set /a avaTime=%limitTime%
echo %date% %time:~0,8%>>debug.txt
echo 进程超时终止 from runJudge.bat>>debug.txt
goto end
)
)
)
:checkTimeLimit
rem 检查时间限制
if %avaTime% GEQ %limitTime% (
rem 超时
set /a resultStatus=3
goto end
)
rem 比较测试结果和正确结果
:compareJudge
fc %outputFile% %judgeFile% >nul
if %errorlevel% equ 0 (
rem 评测通过
set /a resultStatus=0
) else (
rem 答案错误
set /a resultStatus=2
)
:end
echo %date% %time:~0,8%>>log.txt
echo 题目评测结果errorlevel:%errorlevel%,ID:%submitProblemId%>>log.txt
echo executeTime:%avaTime%>>%resultFile%
rem 状态一定在最后输出
echo nowStatus:%resultStatus%>>%resultFile%
exit
timeOverExit.bat
@echo off
rem 程序超时判断 防止程序超时
rem 获取传入参数
set processName=%1
set resultFile=%2
set /a exitTime=%3
rem 设置等待8s关闭评测程序
rem 每个测试用例大概要1s 最多5个用例 选取易错用例
rem ping 127.0.0.1 -n 5 >nul
rem choice指令可以定时
rem 判断参数
if "%processName%" equ "" goto errEnd
if "%resultFile%" equ "" goto errEnd
if "%exitTime%" equ "" goto errEnd
choice /t %exitTime% /d y /n >nul
set /a isFind=0
:loop
for /f "tokens=1 delims= " %%i in ('tasklist ^|findstr "%processName%"') do (
if "%%i" neq "" (
taskkill /im %processName% /f
echo %date% %time:~0,8%>>debug.txt
echo 程序运行超时%exitTime%s>>debug.txt
echo %%i进程已终止>>debug.txt
echo executeTime:%exitTime%000>>%resultFile%
echo nowStatus:3>>%resultFile%
rem 循环检测保证程序已被终止
set /a isFind+=1
goto loop
) else (
echo %date% %time:~0,8%>>debug.txt
echo 超时终止程序结束>>debug.txt
echo %processName%进程未找到>>debug.txt
goto end
)
)
goto end
:errEnd
echo %date% %time:~0,8%>>debug.txt
echo 空参数 from timeOverExit>>debug.txt
:end
rem echo 程序结束 终止进程次数%isFind%
exit
getMemory.bat
rem @echo off
rem 获取进程运行消耗内存
rem 入参为文件名
set processName=%1
set resultFile=%2
:init
rem 判断参数
if "%processName%" equ "" goto errEnd
if "%resultFile%" equ "" goto errEnd
for /l %%a in (1,1,10) do (
for /f "tokens=5 delims= " %%i in ('tasklist ^|findstr "%processName%"') do (
echo %date% %time:~0,8%>>out.txt
echo %%a>>out.txt
for /f "tokens=1,2 delims=," %%j in ("%%i") do (
if "%%j%%k" neq "" (
echo executeMemory:%%j%%k>>%resultFile%
goto end
)
)
)
)
:err
echo %date% %time:~0,8%>>debug.txt
echo 获取%processName%内存失败>>debug.txt
goto end
:errEnd
echo %date% %time:~0,8%>>debug.txt
echo 空参数 from getUserMemory>>debug.txt
:end
exit
updateDatabase.bat
@echo off
rem 更新数据库的函数
:readInfo
rem 读取编译错误信息
setlocal enabledelayedexpansion
set judgeMessage=未知错误
if %2 equ 0 set judgeMessage=评测通过
if %2 equ 1 set judgeMessage=%3
if %2 equ 2 set judgeMessage=答案错误
if %2 equ 3 set judgeMessage=运行超时
if %2 equ 4 set judgeMessage=内存超限
:updateSQL
rem 数据库名
set database=onlinejudge
rem 表名
set table=submit_code_table
set id=%1
rem 登录指令
set "loginSQL=mysql --user=root --password=YOURPASSWORD"
set "querySQL=update %table% set judge_status=%2,judge_message='%judgeMessage%',execute_time=%4,execute_memory=%5 where id=%id%"
set "unknownErrSQL=update %table% set judge_status=2,judge_message='unknown error' where id=%id%"
if "%id%" equ "" (
echo %date% %time:~0,8%>>debug.txt
echo ID不能为空>>debug.txt
echo ID不能为空
goto end
)
echo %date% %time:~0,8%>>sqlLog.txt
(
echo use %database%;
@echo %querySQL%;
)>>sqlLog.txt
(echo use %database%;%querySQL%;)|%loginSQL%
if %errorlevel% equ 0 (
echo %date% %time:~0,8%>>log.txt
echo 更新数据库题目状态成功,ID:%id%>>log.txt
) else (
echo %date% %time:~0,8%>>log.txt
echo 数据库更新出错: %errorlevel%,ID:%id%>>log.txt
echo 请查看错误日志databaseErr.txt>>log.txt
goto errUpdate
)
goto end
rem 判题程序错误则先更新题目状态为4
:errUpdate
(echo use %database%;%unknownErrSQL%;)|%loginSQL%
:end
后言
刚开始准备做判题是想用python的,临时起意用了Bat,对于一个刚入门脚本编写的新手这也算练练手吧,bug心情好就修复更新下吧,欢迎各位留言交流…趁没有人我是菜鸡(*^▽^*)
附上完整源码GitHub:TSOJ源码