Expect脚本远程自动化安装RPM时Bash变量引用报错的排查与解决
问题描述
我正在用Expect脚本实现RPM包的远程自动化安装,但在将Bash变量引入Expect脚本时遇到了问题。我希望获取远程主机的hostname并赋值给变量,之后在sed命令中使用该变量,但运行脚本时出现错误:can't read "(hostname)": no such variable。以下是我的原始脚本:
#!/usr/bin/expect set timeout 15 set operador "operador" set password_operador "a123456" set password_root "123456a" set Zabbix_IP "10.1.10.10" #set Zabbix_IP [exec hostname -I] #set Zabbix_IP [exec hostname -I|cut -f1 -d' '] set f [open "hosts"] set hosts [split [read $f] "\n"] close $f foreach host $hosts { if { ! ( $host == "" ) } { spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $operador@$host expect "password: " send "$password_operador\r" expect "$ " send "su -\r" expect ": " send "$password_root\r" send "rpm -iv /home/operador/Zabbix_Packages_Configs/zabbix-agent2-5.0.2-1.el6.x86_64.rpm \r" send "sleep 1 \r" expect "# " send "V_HostNameZabbix=$(hostname)_SRV \r" send "V_MetaData=TimBR_Linux_SRV_SPO \r" send "sed -i \"s/^Server=127.0.0.1/Server=$Zabbix_IP/g\" /etc/zabbix/zabbix_agent2.conf \r" send "sed -i \"s/^ServerActive=127.0.0.1/ServerActive=$Zabbix_IP/g\" /etc/zabbix/zabbix_agent2.conf \r" send "sed -i \"s/^Hostname=Zabbix server/Hostname=$V_HostNameZabbix/g\" /etc/zabbix/zabbix_agent2.conf \r" expect "# " send "exit\r" expect "$ " send "exit\r" } }
问题原因
这个错误的核心是Expect的变量解析机制和Bash的变量语法冲突:
- Expect会把
$(hostname)中的$当成自己的变量引用符号,试图解析(hostname)这个Expect变量,但它根本不存在,所以直接报错。 - 同样,
$V_HostNameZabbix也会被Expect优先解析,但这个变量在Expect脚本里没有提前定义,后续执行也会触发未找到变量的错误。
解决方案
要解决这个问题,我们需要让Expect把Bash的变量语法原封不动地传给远程Shell执行,同时在Expect中预先声明相关变量避免解析报错:
步骤1:在Expect脚本开头声明空变量
在脚本开头添加以下两行,让Expect识别这些变量名,避免解析时触发未找到变量的错误:
# 设置空变量,避免Expect解析时报错 set V_HostNameZabbix "" set V_MetaData ""
步骤2:转义Bash命令中的$符号
对于需要在远程Bash中执行的变量引用(比如$(hostname)和$V_HostNameZabbix),要在$前面加反斜杠\,让Expect不解析这个$,直接把完整的Bash命令传给远程Shell执行:
# 转义$(hostname)中的$,让远程Bash执行hostname命令 send "V_HostNameZabbix=\$(hostname)_SRV \r" # 转义$V_HostNameZabbix中的$,让远程Bash使用自己的变量 send "sed -i \"s/^Hostname=Zabbix server/Hostname=\$V_HostNameZabbix/g\" /etc/zabbix/zabbix_agent2.conf \r"
修正后的完整脚本
#!/usr/bin/expect set timeout 15 set operador "operador" set password_operador "a123456" set password_root "123456a" set Zabbix_IP "10.1.10.10" # 设置空变量,避免Expect解析时报错 set V_HostNameZabbix "" set V_MetaData "" #set Zabbix_IP [exec hostname -I] #set Zabbix_IP [exec hostname -I|cut -f1 -d' '] set f [open "hosts"] set hosts [split [read $f] "\n"] close $f foreach host $hosts { if { ! ( $host == "" ) } { spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no $operador@$host expect "password: " send "$password_operador\r" expect "$ " send "su -\r" expect ": " send "$password_root\r" send "rpm -iv /home/operador/Zabbix_Packages_Configs/zabbix-agent2-5.0.2-1.el6.x86_64.rpm \r" send "sleep 1 \r" expect "# " send "V_HostNameZabbix=\$(hostname)_SRV \r" send "V_MetaData=TimBR_Linux_SRV_SPO \r" send "sed -i \"s/^Server=127.0.0.1/Server=$Zabbix_IP/g\" /etc/zabbix/zabbix_agent2.conf \r" send "sed -i \"s/^ServerActive=127.0.0.1/ServerActive=$Zabbix_IP/g\" /etc/zabbix/zabbix_agent2.conf \r" send "sed -i \"s/^Hostname=Zabbix server/Hostname=\$V_HostNameZabbix/g\" /etc/zabbix/zabbix_agent2.conf \r" expect "# " send "exit\r" expect "$ " send "exit\r" } }
这样修改后,脚本就能正常运行,远程主机的hostname会被正确捕获并用于sed命令的替换操作。
内容的提问来源于stack exchange,提问作者Anderson Silva
相关产品推荐
相关产品推荐

