适配新旧SSH版本的Expect SSH脚本调整方案咨询
解决Expect脚本兼容新旧SSH设备的问题
你修改后的脚本出现异常,核心原因是捕获到密钥交换错误后,直接启动新SSH进程,但Expect仍在监听原进程的输出,加上exp_continue的循环逻辑,导致新旧进程输出混杂,交互逻辑混乱。
以下是两种可靠的修改方案:
方案1:嵌套Expect块处理重连逻辑
直接在捕获到密钥交换错误时,关闭旧进程并启动新进程,用嵌套的Expect块处理新进程的交互:
# 先尝试默认参数连接新设备 spawn ssh -o StrictHostKeyChecking=no $username\@$host expect { # 捕获密钥交换失败的提示,触发重连 "no matching key exchange" { close wait # 用旧密钥交换算法重新连接 spawn ssh -o KexAlgorithms=diffie-hellman-group1-sha1 -o StrictHostKeyChecking=no $username\@$host # 处理新进程的交互 expect { timeout { send_user "\nTimeout Exceeded - Check Host\n" exit 1 } eof { send_user "\nSSH Connection To $host Failed\n" exit 1 } "*#" {} "*assword:" { send "$password\n" expect "*#" } } } # 原有的超时、EOF处理 timeout { send_user "\nTimeout Exceeded - Check Host\n" exit 1 } eof { send_user "\nSSH Connection To $host Failed\n" exit 1 } # 正常交互逻辑 "*#" {} "*assword:" { send "$password\n" expect "*#" } }
关键细节
- 用
close+wait关闭旧进程,避免产生僵尸进程 - 密码发送后添加
expect "*#",确保脚本等待到设备提示符后再结束,防止提前退出
方案2:封装连接函数(更简洁)
把连接逻辑封装成过程,通过递归实现自动重试旧算法,避免重复代码:
# 定义SSH连接过程 proc ssh_connect {username host password use_old_kex} { set ssh_cmd "ssh -o StrictHostKeyChecking=no" # 如果需要旧算法,追加参数 if {$use_old_kex} { append ssh_cmd " -o KexAlgorithms=diffie-hellman-group1-sha1" } append ssh_cmd " $username\@$host" spawn $ssh_cmd expect { timeout { send_user "\nTimeout Exceeded - Check Host\n" exit 1 } eof { if {$use_old_kex} { # 用旧算法仍失败,直接退出 send_user "\nSSH Connection To $host Failed Even With Old Kex\n" exit 1 } else { # 第一次失败,尝试旧算法重连 close wait ssh_connect $username $host $password 1 } } "no matching key exchange" { # 捕获密钥交换错误,切换旧算法重连 close wait ssh_connect $username $host $password 1 } "*#" { return } "*assword:" { send "$password\n" expect "*#" return } } } # 调用连接函数,默认使用新设备参数 ssh_connect $username $host $password 0
优势
- 代码复用性更高,后续修改交互逻辑只需调整一处
- 自动处理两种连接场景,无需手动分支判断
内容的提问来源于stack exchange,提问作者Yohan Oc
相关产品推荐
相关产品推荐

