如何使用Expect脚本屏蔽Telnet登录成功前的终端输出
初始测试脚本
需求为在Bash环境中调用Expect实现自动登录IP为192.168.1.11的Telnet服务,执行ls命令,要求屏蔽Telnet成功登录前输出到本地终端的内容,初始测试脚本如下:
expect << EOS spawn telnet 192.168.1.11 expect "login:" send "root\r" expect "#" send "ls\r" expect "#" send "exit\r" expect eof EOS
直接执行上述脚本会完整输出Telnet连接过程、登录提示、命令执行回显等全部内容,测试过程中遇到三类典型问题:
- 直接在
spawn指令前添加log_user 0;配置,会导致登录成功后ls等命令的输出也被全部屏蔽,不符合需求 - 调整
log_user开关位置,在发送ls命令前开启log_user、匹配到命令提示符后关闭log_user,仍无法屏蔽ls命令自身的回显行 - 尝试通过正则规则
expect -re "$command\r\n(.+)\r\n$prompt"捕获命令执行结果时,出现匹配超时、无法正确读取$expect_out(1,string)缓存中命令输出的问题,即使将变量传参的命令替换为硬编码pwd仍无法正常获取返回结果,当前使用Expect版本为5.45.3
问题原因与修复方案
1. log_user全局屏蔽问题
log_user 0会直接关闭所有spawn进程输出回显到当前终端的通道,不会自动区分登录阶段和命令执行阶段,需要在匹配到登录成功的Shell提示符(即脚本中第一次匹配到#的节点)之后再执行log_user 1开启输出,即可保证登录前的连接过程、登录提示全部被屏蔽,登录后的内容正常输出。
2. 命令自身回显无法屏蔽问题
Telnet服务默认开启终端行回显,发送的命令字符串会被Telnet服务端先回显一行再输出执行结果,仅靠log_user开关无法过滤这行回显。解决方法是在发送命令后,先匹配命令自身的回显内容丢弃,再等待命令提示符,同时捕获中间的输出内容。
3. 正则匹配命令输出超时问题
Expect 5.45.3版本的正则引擎本身不存在\r匹配缺陷,之前捕获失败的核心原因是没有提前消费掉缓冲区里的命令回显行,导致正则匹配的起始位置偏移,最终触发超时。同时Expect正则默认是贪婪匹配,不同Telnet服务端输出的换行符可能是\r\n或者单独\r,提示符前可能存在多余的空白字符,硬编码的\r\n分隔规则很容易匹配失败。编写匹配规则时需要先丢弃命令回显,再兼容不同换行格式,必要时可通过匹配配置降低规则敏感度。
修正后可直接运行的脚本
expect << EOS # 登录阶段关闭所有输出 log_user 0 spawn telnet 192.168.1.11 expect "login:" send "root\r" # 匹配到登录成功的Shell提示符后,开启输出 expect "#" log_user 1 # 执行ls命令,先过滤命令自身回显 send "ls\r" # 先匹配丢弃ls命令的回显行 expect -re "ls\r\n" # 捕获命令输出直到遇到提示符,兼容\r、\r\n换行格式 expect -re "(.+)\r?\n.*# " # 如需获取命令输出可读取$expect_out(1,string),此处直接让输出正常打印即可 send "exit\r" expect eof EOS
注意:如果实际环境中Shell提示符不是
#(比如带主机名、路径的自定义提示符),需要将匹配规则中的#替换为实际的提示符特征字符串,避免匹配失败。
内容的提问来源于stack exchange,提问作者John

