Expect脚本批量配置远程服务器UFW规则时获取内网IP报错排查
问题原因
报错核心是Expect(基于Tcl语法)的变量解析规则冲突:
- 你写在ssh远程命令里的
$(...)shell命令替换,会被Tcl识别成本地变量引用,直接尝试读取括号内命名的变量,不会把这段逻辑传给远程服务器执行,因此触发「找不到对应变量」的错误。 - 原脚本里grep匹配IP的正则多写了无意义的转义方括号,即使解决解析问题也无法正确提取私网IP。
修复方案
需要做4处核心调整:
- 给远程命令里的
$加反斜杠转义为\$,告诉Tcl不要解析这部分内容,原样传给远程shell执行 - 修正grep提取IP的正则错误,补全反斜杠转义避免被Tcl吞掉转义符
- 补充sudo密码提示的匹配规则,避免执行sudo时卡住
- 调整超时逻辑,单台服务器连接超时不中断整个批量流程,补充eof捕获避免脚本执行后挂死
修复后的完整脚本如下:
#!/usr/bin/expect # 采集SSH登录用户名 send_user "user: " expect_user -re "(.*)\n" set user $expect_out(1,string) # 采集登录密码(关闭输入回显) stty -echo send_user -- "Password for $user: " expect_user -re "(.*)\n" send_user "\n" stty echo set pass $expect_out(1,string) # 遍历指定私网网段 for {set i 1} {$i < 10} {incr i 1} { spawn ssh -t $user@192.168.1.$i "sudo ufw allow from X.X.X.X to \$(ip a |grep '192.168.1' | grep -Po 'inet \\K[\d.]+') port YYYY" set timeout 5 expect { timeout { puts "Connection to 192.168.1.$i timed out, skip" close continue } "yes/no" { send "yes\r" exp_continue } "assword:" { send -- "$pass\r" exp_continue } "*sudo*password*" { send -- "$pass\r" exp_continue } eof { puts "UFW rule added on 192.168.1.$i successfully" } } }
使用注意事项
- 使用前把脚本里的
X.X.X.X替换为实际需要放通的源IP/网段,YYYY替换为要开放的对应端口 - 如果需要遍历更大的网段,直接修改for循环的终止条件即可,比如要覆盖1-254地址,就把
{$i < 10}改成{$i <= 254} - 如果你的私网网段不是192.168.1.0/24,同步修改ssh连接地址段、grep过滤的网段关键字即可
内容的提问来源于stack exchange,提问作者Ror
相关产品推荐
相关产品推荐

