shell 使用 expect 编写远程执行脚本

expect 作为自动交互工具,在 shell 里应用广泛,在设置密码的情况下,可以模拟人工流程对远程主机执行指定操作,大大简化了登录流程,降低了运维成本。但如果直接把功能写在一个脚本,密码要明文定义,不是很安全,而且其他地方调用也不方便,既然这样,何不把 expect 远程登录执行写成一个函数,供其他脚本快捷调用呢,在方便的同时,也可以通过权限控制,有效地避免密码泄露。下面就来说说如何把 expect 远程执行功能变成一个可调用的函数。

  • 首先,为了能在远程主机上成功执行命令,需要写一个 check 函数( EXPECT_CHECK.sh ):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env bash
#expect 功能检查
set -e;

EXPECT_CHECK(){
local EXUSR=${1}
local EXHOST=${2}
local EXPWD=${3}
#ssh test
EXP_RST=`
expect -c "
set timeout 300
spawn ssh ${EXUSR}@${EXHOST} \"echo PASS\"
expect {
\"not known\" {send_user \"[exec echo -e Erro:Host not known\n];exit\"}
\"Connection refused\" {send_user \"[exec echo -e Erro:Connection refused\n];exit\"}
\"(yes/no)?\" {send \"yes\r\";exp_continue}
\"password:\" {send \"${EXPWD}\r\";exp_continue}
\"Permission denied\" {send_user \"[exec echo -e Erro:Wrong passwd\n];exit\"}
}
"|grep -E 'PASS|Erro'|grep -v echo|sed 's/\r//g;s/\n//g'
`
if [[ ${EXP_RST} && ${EXP_RST} == PASS ]]; then
echo -e "\nEXPECT CHECK COMPLETE!\n";
return 0;
else
echo -e "\n${EXUSR}@${EXHOST} EXPECT CHECK ERROR!\n";
echo -e "\n${EXP_RST}\n";
return 1;
fi
}
  • 如果 check 函数执行检查成功,就可以正式执行我们的命令了( EXPECT_SH.sh ):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env bash
#远程执行命令。
#这里只判断了]和>结束符,如果远程主机是其他结束符,还需要自行添加。
set -e;

EXPECT_SH(){
local EXUSR=${1}
local EXHOST=${2}
local EXPWD=${3}
local EXCMD=${4}
expect -c "
set timeout 300
spawn ssh ${EXUSR}@${EXHOST}
expect {
\"not known\" {send_user \"[exec echo -e Erro:Host not known\n];exit\"}
\"Connection refused\" {send_user \"[exec echo -e Erro:Connection refused\n];exit\"}
\"(yes/no)?\" {send \"yes\r\";exp_continue}
\"password:\" {send \"${EXPWD}\r\";exp_continue}
\"Permission denied\" {send_user \"[exec echo -e Erro:Wrong passwd\n];exit\"}
\"]*\" {send \"\r\"}
\">*\" {send \"\r\"}
}
send \"${EXCMD}\rexit\r\"
expect eof
"
}
  • 最后便是函数的调用:
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash
set -e
cd `dirname $0`

#以下变量均在ENV.sh定义

. ./ENV.sh 2> /dev/null;
. ./EXPECT_CHECK.sh
. ./EXPECT_SH.sh

EXPECT_CHECK ${USER} ${IP} ${PW};
EXPECT_SH ${USER} ${IP} ${PW} "cd ${SERVER_BIN};./${CONTROL_SCRIPT} REPLACE_BY_ENV restart";