需要帮助使用 expect 从不断变化的日志文件中提取特定行

Need help extracting specific lines from a changing logfile using expect

我正在尝试使用 expect 脚本通过 telnet 访问远程设备,read/save 本地远程 "EVENTLOG",然后从日志文件中提取特定行(序列号)。问题是日志文件不断变化,所以我需要一种方法来搜索特定的字符串。远程设备基于 Linux,但没有 grep、vi、less 等,因为它是 QNX Neutrino,因此必须在本地进行。

我已经成功获得 telnet,读取文件并在本地控制下保存,但是当我到达 "reading" 时文件出现问题。目前我只是想让它打印它发现的内容,但脚本只是退出而不报告任何东西,除了一些额外的大括号??

#!/usr/bin/expect -f

set timeout -1
    log_user 1

    spawn telnet $IP
    match_max 100000
    expect "login:"
    send -- "$USER\r"
    expect "Password:"
    send -- "$PW\r"
    expect "# "
    send -- "\r"

#at this point logged into device
#send command to generate the "dallaslog"

    set dallaslog [open dallaslog.txt w]
    expect "#" 
        send -- "cat `ls -rt /LOG/event*`\r"

    expect "(cat) exited status=0"
    set logout $expect_out(buffer)
    puts $dallaslog "$logout"
    close $dallaslog
    unset expect_out(buffer)

    set dallasread [open dallaslog.txt r]
    set lines [split [read $dallasread] "\r"]
    close $dallasread

    puts "${green}$lines{$normal}"
#a debug line to print $dallasread in green so I can verify it works up to here

    foreach line $lines {
        if {[regexp {.*Dallas ID: 0.*\n} $lines match]} {
        if {$match == 1} {
            puts $line  ;# Prints whole line which has 1 at end 
        }
        }
    }
    expect "# "
    send -- "exit\r"
    interact

我(最终)寻找的是脚本来捕获以 "Dallas ID:" 开头的任何行,然后将该信息保存到变量中,这样我就可以使用 "scan" 命令来解析行并提取信息。

我得到的是:

(the results from $lines being "puts" in green)
"...
<ENTRY TIME="01/01/1970 00:48:07" PROC="syncd" FILE="mips.cc" LINE="208" NUM="10000">
UTC step from 01/01/1970 00:48:08 to 01/01/1970 00:48:07
</ENTRY>

Process 3174431 (cat) exited status=0
}{}
# exit

Process 3162142 (sh) exited status=0.
Connection closed by foreign host."

提前感谢您提供的所有帮助。我是 TCL/expect 的新手(自去年​​ 7 月以来一直在玩弄它),但我发现它是一个非常强大的工具,只是我很难调试!

编辑:根据@meuh 的回复添加了更多信息。 示例:最多可以有4个Dallas ID,但一般我只有0和1。目标是获取到达拉斯ID的SN,BC,CN作为变量保存在单独的文本文件中。

<ENTRY TIME="01/01/1970 00:00:06" PROC="sys" FILE="PlatformUtils.cpp" LINE="1227" NUM="10044">
Dallas ID: 1 SN:00000622393A BC: J4AD945 CN: IS200BPPBH2BMD R0: 001C 
</ENTRY>

我使用的 foreach 循环是一个关于堆栈溢出的老问题的例子,我试图修改以在这里使用,但没有成功。

编辑:我还应该提一下,这个事件日志每次被阅读时大约有 800 行长,这就是为什么我没有发布它的摘录。

此正则表达式行可能没有按照您的要求执行:

if {[regexp {.*Dallas ID: 0.*\n} $lines match]} {
    if {$match == 1} {
        puts $line  

您传递的是列表 $lines 而不是单行 $line。变量 match 将被设置为匹配的字符串,因此必须包含单词 "Dallas" 等等,因此它永远不会是 1。 您的代码注释说 Prints whole line which has has 1 at end,但我不确定您在寻找什么,因为您没有任何适合正则表达式的示例数据。

如果您选择使用分组的正则表达式模式,您可以捕获部分行,因此可能不需要进一步 scan。例如

regexp {PROC="([a-z]*)"} $line match submatch

会在上面的示例中将变量子匹配设置为 syncd


您可能还遇到了一个基本问题,该问题是由 tcl 对文件输入的 \r\n 处理引起的。你从 $expect_out(buffer) 得到的行确实有 2 个字符作为行尾分隔符。然而, 当使用 read 时,我相信默认情况下,它会将相同的序列转换为规范化的 \n。所以你的拆分不会做任何事情,你需要在 \n 而不是 \r 上拆分。您可以使用

检查行列表的大小
puts [llength $lines]

如果为 1,则说明您的拆分无效。将其替换为

set lines [split [read $dallasread] "\n"]

这应该有助于你的循环,例如你可以尝试

foreach line $lines {
    if {[regexp {.*Dallas ID: (\d+) SN:([^ ]+)} $line match idnum SN]} {
        puts $line
        puts "$idnum, $SN"
    }
}    

您必须删除正则表达式末尾的 \n,因为它在拆分后不再存在。我用 (\d+) 扩展了正则表达式示例以匹配 ID 号(\d 匹配一个数字),并使用 ([^ ]+) 匹配之后任意数量的非 space 字符文本 SN:

这些值是通过使用 () 分组捕获的,并放置在变量 idnum 和 SN 中,您应该能够在第二个 puts 命令中看到输出。