我正在尝试在大型文本文件中搜索字符串并相应地返回退出代码。对于上下文,文本文件是一个ModelSim日志文件,我需要将退出代码1(错误(或0(良好(传递给调用以下TCL脚本(代码段(的Windows批处理脚本:
set sim_pass {# ** Failure: NONE. End of Test: PASS}
set sim_fail {# ** Failure: ERRORS. End of Test: FAIL}
# set each_line "# ** Failure: ERRORS. End of Test: FAILr";
set failed_sim 0
# Set top-level paths and testbench.
global app_sim_path reg_tb_path parent_path log_directory msim_ini
set TOP_LEVEL_NAME project_io_tb
cd $app_sim_path
# Create logfile.
set sim_log_file ${parent_path}/${log_directory}/${TOP_LEVEL_NAME}_sim.log
if {[file exists $sim_log_file]} {
file delete -force $sim_log_file
}
set fs [open $sim_log_file {RDWR CREAT EXCL}]
close $fs
# Initialize ModelSim.
puts "[INFO] Now testing ... $TOP_LEVEL_NAME"
vsim -modelsimini $msim_ini -c -quiet -l $sim_log_file
# Compile all files required for testbench.
source compile.tcl
# Log all signals for debug.
log -r /*
# Run simulation.
run -all
quit -sim
# Read log file.
set fptr [open ${sim_log_file} r]
set log_data [read $fptr]
close $fptr
# Split file contents on new line.
set line_data [split $log_data "n"];
# Parse each line.
foreach each_line $line_data {
# Check if simulation test failed.
if {[string match $sim_fail [string trim $each_line]]} {
set failed_sim 1
break
}
}
# Return correct exit code.
if {$failed_sim} {
puts "Simulation Failed, returning $failed_sim"
} else {
puts "Simulation Passed, returning $failed_sim"
}
exit -code $failed_sim
quit -f
当我执行上述脚本时,至少有 2 件事出错:
- 日志文件将回显到我的 Windows 命令外壳,不带换行符。如果我注释掉"#读取日志文件"之后的所有内容,则不会发生这种情况。
- 即使字符串出现在日志文件中,退出代码也为 0。
我做错了什么?
[编辑]: 感谢大家的回复。不幸的是,我仍然无法让我的 TCL 脚本工作。这可能与我的日志文件包含我正在搜索的以下文本字符串有关:
# ** Failure: ERRORS. End of Test: FAIL
我无法更改文本的第一部分"# ** 失败:",因为这是 ModelSim 用于日志文件的语法。所以"**"可能会搞砸字符串匹配,因为我不知道如何转义它。
我真正想做的是搜索子字符串"错误。测试结束:失败",这是我可以更改的文本。
请注意,我在foreach循环中使用字符串命令。
我仍然将整个日志文件的疯狂长回声发送到我的 Windows 命令窗口,该窗口也写入没有换行符的日志文件。恐怕我没有正确打开/关闭日志文件,因此在我的原始代码片段中添加了更多内容。
好吧,这些模式的问题:
set sim_ok {# ** Failure: NONE. End of Test: PASS};
set sim_fail {# ** Failure: ERRORS. End of Test: FAIL};
对于string match
支持的glob 模式,您必须匹配整个字符串,即使在拆分成行之后,其中也可能出现意外字符。其中可能还有其他您意想不到的东西(例如,颜色代码(,但这些最终仍然是仍然需要匹配的字符。此外,您可能在行尾有空格,并且 glob 模式中的**
与*
相同,并且匹配任意数量的任何字符。
诊断提示
您可以通过执行以下操作来发现此类问题:
puts [exec od -c << $the_line_contents]
但是你要小心这样做,因为它大大增加了输出量。这是一个诊断工具,可以让您准确查看匹配的内容并轻松查找异常字符,因为
od -c
将任何不可打印的 ASCII 转换为转义序列并将所有内容分离出来,但它不是您(通常(留在生产使用中的东西。
请尝试以下模式:
# BTW, you don't need semicolons at the end of the line
set sim_ok {# * Failure: NONE. End of Test: PASS}
set sim_fail {# * Failure: ERRORS. End of Test: FAIL}
并使用以下方法进行模式匹配:
if {[string match $sim_fail [string trim $each_line]]} {
...
处理彩色输出的最简单方法(如果这是一个问题(是在运行生成输出的程序之前,将TERM
环境变量更改为不支持颜色的内容,例如vt100
或plain
。
exit
命令不采用-code
选项。只需使用直接exit 1
即可执行此程序失败的退出。
第一个问题是由-nonewline引起的。删除它。
set log_data [read $fptr]
如果保留它,则log_data将不包含换行符,因此split $log_data "n"
将是长度为 1 的列表。
这可能是退出命令的-code
选项。我找到的文档表明它不是一个受支持的选项(没有用于退出的选项(。您以前可能在其中有一个return
语句,它确实支持 -code 属性。尝试
exit $failed_sim
对于匹配精确的子字符串(不是正则表达式或 glob 模式(,[string first]
是一个很好的工具:
set sim_fail {# ** Failure: ERRORS. End of Test: FAIL};
set each_line "# ** Failure: ERRORS. End of Test: FAILr" ;# trailing whitespace
if {[string match $sim_fail $each_line]} {puts matched} else {puts "not matched"}
# => not matched
if {[string first $sim_fail $each_line] != -1} {puts matched} else {puts "not matched"}
# => matched
在对我的 TCL 脚本进行了更多调试后,我让它工作了。解决方案是:
-
在我的文件前面添加"悄悄地"读:
quietly set line_data [split [read -nonewline $fptr] "n"]
这将关闭来自ModelSim的成绩单回显,ModelSim是我正在使用的TCL解释器。
-
修复了"break"命令的缩进和错位:
foreach each_line $line_data { if {[string match $sim_fail $each_line]} { set failed_sim 1 break } }
感谢您的帮助!