问题描述
设置:Linux 中的 Expect/Tcl 脚本
用例:
使用 expect
等待在 $user_command
中使用的某些状态的报告。
expect -re "notify (.+)\n"
set status $expect_out(1,string)
send [string map [list SESSION "$status"] "$user_command"]
因此,当应用程序发送“notify running
”时,status
将设置为 running
。
为此,STATUS
中的关键字 $user_command
需要替换为 $status
,例如
"log STATUS to file"
变成
"log running to file"
为了看看发生了什么,我写了
expect_tty -re "(.+)\n"
set status $expect_out(1,string)
send_user [string map [list SESSION "$status"] "$user_command"]
在单独运行时工作正常。输出是
log someUserInput to file
在输入 someUserInput
以响应 expect_tty
时。但是,作为较大脚本的一部分,字符串映射命令会在字符串替换之前删除任何内容,因此输出变为
" to file"
(没有换行)我检查了脚本中变量的唯一性,所以这不是问题。
问题:
这里发生了什么?如何使脚本健壮?
解决方法
string map
命令是完全确定的。在其输入字符串的每个字符处,它会按顺序考虑映射列表中的任何 from 字符串是否匹配,如果匹配,则执行替换(使用成对的 to 字符串)并继续考虑紧跟在替换子字符串之后的字符。 (空字符串是一种特殊情况:它从不匹配。)实现它的代码真的很愚蠢,但碰巧在现代计算机上对缓存非常友好,所以它仍然非常快;已经尝试了更复杂和据称“更快”的实现,但发现在实践中使用通常在野外遇到的地图类型会更慢。
如果替换失败,通常是因为输入字符串与您期望的不符。在大多数程序中,这种情况很少见,但在 expect 程序中更为常见,因为其中的终端仿真引擎的输出可以包含用于移动光标和更改颜色之类的元字符。 (通常最简单的解决方法是告诉生成的程序它的终端类型不支持如此复杂的功能,也许通过将 TERM
环境变量设置为 dumb
。)
感谢@glennjackman:
问题是由于应用程序将换行符报告为 \r\n
,因此 (.*)\n
in
expect_tty -re "(.+)\n"
在表达式 1(括号内的东西)中匹配最后包含 \r
的东西。随着 \r
删除它之前的任何内容,string map
似乎在被替换的字符串之前剪切任何内容。解决方案是期望排除\r
,即
expect -re ``(\[^\r\n\]+)``
收集任何内容直到行尾,无论格式如何。