TCL 字符串匹配不起作用



我正在尝试在大型文本文件中搜索字符串并相应地返回退出代码。对于上下文,文本文件是一个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 件事出错:

  1. 日志文件将回显到我的 Windows 命令外壳,不带换行符。如果我注释掉"#读取日志文件"之后的所有内容,则不会发生这种情况。
  2. 即使字符串出现在日志文件中,退出代码也为 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环境变量更改为不支持颜色的内容,例如vt100plain


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 脚本进行了更多调试后,我让它工作了。解决方案是:

  1. 在我的文件前面添加"悄悄地"读:

    quietly set line_data [split [read -nonewline $fptr] "n"]
    

这将关闭来自ModelSim的成绩单回显,ModelSim是我正在使用的TCL解释器。

  1. 修复了"break"命令的缩进和错位:

    foreach each_line $line_data {
    if {[string match $sim_fail $each_line]} {
    set failed_sim 1
    break
    }
    }
    

感谢您的帮助!

最新更新