记录使用 expect
遇到的一个坑
1. 问题描述
最近写了一个自动化的安装程序,由于要远程操作多台服务器进行安装,所以必然想到了使用 Shell 脚本来自动化。而设计到密码输入的(比如 sudo
)则使用 expect
来进行交互,然后被一个小坑浪费了一个下午的时间。
先上代码:
#!/bin/bash
user=${1}
ip=${2}
passwd=${3}
expect -c "
spawn ssh ${user}@${ip} -t 'uname -a'
expect {
assword: {
send \"${passwd}\r\"
}
}
expect eof
"
本质上就是连接到服务器执行命令而已,uname -a
但是脚本竟然报错了:zsh:1: command not found: uname -a
(注:真实执行的命令可不是 uname -a
这么简单,这里做示例足够了)。
WTF?
2. 解决过程
想了半天,我一直以为是使用 ssh
登录过去环境变量不对导致的。花了半天时间研究了ssh连接远程主机执行脚本的环境变量问题 这篇文章。虽然没有解决问题,但是文章不错!
苦恼良久,我看到了 send \"${passwd}\r\"
这段代码感到很奇怪。这段 expect
代码是从一个前同事的脚本中扒下来的,整条 expect
命令是由双引号括起来的,字符串参数值替换应该没问题,为什么一定要用转义的双引号呢?
我猜这里可能有什么关键,所以果断把 'uname -a'
改成 \"uname -a\"
。
结果跑通了!
接下来我又发现 'uname -a'
改成 'uname'
后原来的代码也能跑。
测试了一下午加一个晚上总结出,总结出三种可以跑通的情况:
- 不在
expect
命令中而直接以ssh <user>@<ip> <cmd>
的形式调用。 'uname -a'
改成\"uname -a\"
'uname -a'
改成'uname'
所以我猜测应该是在使用 expect
时命令体内部在表示 <cmd>
部分出了问题。当命令带 flag
时,单引号没有起到作用,需要使用转义的双引号。
其底层原因我也没时间去探索了,在此 mark
一下吧。
3. 结论
总结来说,expect
命令内部需要用双引号和单引号的还是尽量用转义的双引号吧!