Expect脚本:如何将远程命令输出拆分至多个变量
解决Expect脚本拆分日志文件名到独立变量的问题
看起来你已经搭好了基础的Expect脚本框架,现在就差把输出的三个日志文件名拆成独立变量,再加上抓取文件的逻辑对吧?我来一步步帮你搞定~
步骤1:从列表中提取独立变量
你的现有脚本里,Last3LogFiles已经是一个包含三个文件名的列表(因为cmd1的输出是每行一个文件名,lappend会把每行内容作为列表的一个元素存起来)。我们可以用Tcl的lindex命令直接从列表里提取每个元素,赋值给单独的变量:
# 在收集完Last3LogFiles之后添加这段代码 if {[llength $Last3LogFiles] == 3} { set log1 [lindex $Last3LogFiles 0] set log2 [lindex $Last3LogFiles 1] set log3 [lindex $Last3LogFiles 2] # 打印验证一下 send_user "拆分后的日志文件名:\n" send_user "1. $log1\n" send_user "2. $log2\n" send_user "3. $log3\n" } else { send_user "出错了:预期获取3个日志文件,但实际得到了[llength $Last3LogFiles]个\n" exit 1 }
步骤2:优化输出捕获逻辑
你原来的捕获输出的代码可以更精准一点,直接匹配每行的文件名(避免捕获多余字符),修改后的代码如下:
# 替换原有的set Last3LogFiles {}到expect -ex $prompt1的部分 set Last3LogFiles {} expect { -re {^(\S+)\r\n} { ;# 匹配每行的非空白字符(也就是文件名) lappend Last3LogFiles $expect_out(1,string) exp_continue ;# 继续捕获下一行 } -ex $prompt1 { ;# 匹配到提示符就停止捕获 # 啥也不用做,直接往下走 } }
步骤3:添加抓取日志文件的逻辑
这里给你两种方案,按需选择:
方案A:在SSH会话内直接下载(适合小文件)
直接在远程执行cat命令,把文件内容传回本地保存:
# 定义本地保存目录,改成你自己的路径 set local_dir "/your/local/logs" # 先确保本地目录存在 if {![file exists $local_dir]} { file mkdir $local_dir } # 逐个下载文件 foreach log [list $log1 $log2 $log3] { send "cat $dir/$log\r" expect -re "^(.+)\r\n$prompt1" { set file_content $expect_out(1,string) # 写入本地文件 set local_file [open "$local_dir/$log" w] puts $local_file $file_content close $local_file send_user "已保存$log到$local_dir\n" } }
方案B:用SCP下载(更稳定,适合大文件)
如果日志文件比较大,用SCP更可靠,你可以在退出SSH会话后执行:
# 先退出当前SSH会话 send "exit\r" expect eof # 逐个用SCP下载 foreach log [list $log1 $log2 $log3] { spawn scp admin@10.30.35.36:$dir/$log $local_dir/ expect { -re ".*Are.*.*yes.*no.*" { send "yes\r" exp_continue } "*?assword:*" { send "$passwd\r" } } expect eof send_user "已通过SCP下载$log\n" }
完整优化后的脚本
#!/usr/bin/expect -f set passwd "xxx" set cmd1 "ls -ltr | tail -3 | awk '{print \$NF}'" set dir "/cluster/storage/var/log/alarms" set local_dir "/your/local/logs" ;# 改成你的本地保存路径 set timeout 10 ;# 适当延长超时时间,避免下载超时 set prompt1 "*\$ " log_user 0 # 建立SSH连接 spawn ssh admin@10.30.35.36 expect { -re ".*Are.*.*yes.*no.*" { send "yes\n" exp_continue } "*?assword:*" { send "$passwd\n" } } # 切换到日志目录 expect $prompt1 { send "cd $dir\r" } # 执行命令获取最近3个日志文件 expect $prompt1 { send "$cmd1\r" } # 捕获文件名到列表 set Last3LogFiles {} expect { -re {^(\S+)\r\n} { lappend Last3LogFiles $expect_out(1,string) exp_continue } -ex $prompt1 } # 拆分到独立变量 if {[llength $Last3LogFiles] == 3} { set log1 [lindex $Last3LogFiles 0] set log2 [lindex $Last3LogFiles 1] set log3 [lindex $Last3LogFiles 2] send_user "成功获取日志文件:\n" send_user "1. $log1\n" send_user "2. $log2\n" send_user "3. $log3\n" } else { send_user "错误:预期3个日志文件,实际得到[llength $Last3LogFiles]个\n" exit 1 } # 确保本地目录存在 if {![file exists $local_dir]} { file mkdir $local_dir } # 方案A:SSH会话内下载(小文件用这个) foreach log [list $log1 $log2 $log3] { send "cat $dir/$log\r" expect -re "^(.+)\r\n$prompt1" { set file_content $expect_out(1,string) set local_file [open "$local_dir/$log" w] puts $local_file $file_content close $local_file send_user "已保存$log到$local_dir\n" } } # 退出SSH会话 send "exit\r" expect eof # 如果你想用方案B的SCP,注释掉方案A的代码,取消下面的注释 # foreach log [list $log1 $log2 $log3] { # spawn scp admin@10.30.35.36:$dir/$log $local_dir/ # expect { # -re ".*Are.*.*yes.*no.*" { # send "yes\r" # exp_continue # } # "*?assword:*" { # send "$passwd\r" # } # } # expect eof # send_user "已通过SCP下载$log\n" # } exit 0
注意事项
- 记得把
$local_dir改成你实际要保存文件的本地路径 - 如果远程服务器的提示符不是
*$,请修改prompt1变量的值,确保能正确匹配 - 可以根据文件大小调整
timeout的值,避免超时错误
内容的提问来源于stack exchange,提问作者Julius
相关产品推荐
相关产品推荐

