【问题标题】:Making Tcl/Expect statements less "greedy"使 Tcl/Expect 语句不那么“贪婪”
【发布时间】:2020-09-28 09:54:29
【问题描述】:

考虑下面的 Expect 脚本。它创建一个测试文件,每行都有一个字符 (a,b,c,a,b,c),然后使用 spawn 打开文件:

#!/usr/bin/expect -f

proc create_test_file {fname} {
    set chan [open $fname w]
    puts $chan "a\nb\nc"
    puts $chan "a\nb\nc"
    catch {close $chan}
}

log_user 0
create_test_file "test_file.txt"

set f [open "test_file.txt" r]
spawn -open $f

while {1} {
    expect {
        "c" { send_user "C\n" }
        "b" { send_user "B\n" }
        "a" { send_user "A\n" }
        eof { break }
    }
}

输出是:

C
C

预期的输出不会逐行处理文件,而是 而是一次。期望语句“c”中的第一行将丢弃第一行与 a 和 b,然后匹配第三行与 c,以此类推进行下一个循环。

其他的 expect 语句也会匹配这些行,但它们永远没有机会这样做,因为第一个语句首先匹配,即使第一个语句丢弃了更多的行。

我想我理解这种行为,但现在我的问题是:有没有办法调整期望的行为,以便匹配的期望语句是需要丢弃文件中最少行数的语句?这样生成的输出将变为以下内容:

A
B
C
A
B
C

【问题讨论】:

  • 有不同的方法来做你描述的事情,但你真正的用例是什么?
  • 注意,你的puts 应该是puts $chan
  • 是的,没错。复制时出错。

标签: tcl expect


【解决方案1】:

默认情况下,模式是未锚定的。您可以使用^ 将它们作为正则表达式锚定到缓冲区的开头。例如,要逐个字符匹配,使用正则表达式 . 跳过不匹配的字符:

while {1} {
    expect {
        -re "^c" { send_user "C\n" }
        -re "^b" { send_user "B\n" }
        -re "^a" { send_user "A\n" }
        -re "^." { exp_continue }
        eof { break }
    }
}

虽然我在上面使用了正则表达式,但在这种情况下没有必要,因为默认的“glob”模式也接受 ^ 表示缓冲区的开始,所以你也可以使用

        "^c" { send_user "C\n" }
        "^b" { send_user "B\n" }
        "^a" { send_user "A\n" }
        "^?" { exp_continue }

其中? 匹配全局模式中的单个字符,相当于正则表达式中的.

【讨论】:

  • 很好,它可以工作,谢谢!我无法理解“^”的必要性。首先但后来我意识到它与换行符匹配。 exp_continue 在这里也是可选的,因为有一个外部循环。
  • > ^ 匹配期望已读取的缓冲区的开头,而不是任何特定字符。是的,我明白,但在你上面的回答中,如果行:-re "^." { exp_continue } 被省略,脚本只会打印一次 A,因为没有任何内容与字符之间的换行符匹配 (a\nb\nc\na\nb\nc\n)。这就是我说它与换行符匹配时的意思。
  • 你是对的,我把"^."误读为"^"!抱歉,我会删除该评论,稍后再删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-07
  • 2017-10-05
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
相关资源
最近更新 更多