expect 安装

1
yum install -y expect

expect工作原理

​ 从最简单的层次来说,Expect的工作方式象一个通用化的Chat脚本工具。Chat脚本最早用于UUCP网络内,以用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。

​ Chat脚本由一系列expect-send对组成:expect等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。例如下面的 Chat脚本实现等待标准输出出现Login:字符串,然后发送somebody作为用户名;然后等待Password:提示符,并发出响应 sillyme。

1
Login: somebody Password: sillyme

Expect最简单的脚本操作模式本质上和Chat脚本工作模式是一样的。

expect简单例子

为了更好理解except脚本几个简单参数,我们再举一个简单的例子:

1
2
3
4
5
6
#!/usr/bin/expect   
set timeout 30
spawn ssh -l username 192.168.1.1
expect "password:"
send "ispass\r"
interact

说明:

1.[#!/usr/bin/expect]

1
这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是	一类东西。注意:这一行需要在脚本的第一行。

2.[set timeout 30]

1
设置超时时间,计时单位是:秒

3.[spawn ssh -l username 192.168.1.1]

1
spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。它主要的功能是给ssh运行进程加个壳,用来传递交互指令。

4.[expect “password:”]

1
这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒

5.[send “ispass\r”]

1
2
这里就是执行交互动作,与手工输入密码的动作等效。  
温馨提示: 命令字符串结尾别忘记加上 “\r”,如果出现异常等待的状态可以核查一下。

6.[interact]

1
执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]

expect实用案例

远程登录到linux,并且执行命令,执行完后并退出

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/expect -f 
set ip 192.168.1.130
set password admin
set timeout 10
spawn ssh root@$ip
expect {
"*yes/no" { send "yes\r"; exp_continue}
"*password:" { send "$password\r" }
}
expect "#*"
send "pwd\r"
send "exit\r"
expect eof

运行结果如下:

1
2
3
4
5
6
7
8
9
1. root@nmk# ./test.exp 
2. spawn ssh root@192.168.1.130
3. root@192.168.1.130's password:
4. Last login: Fri Sep 7 14:05:07 2012 from 116.246.27.90
5. [root@localhost ~]# pwd
6. /root
7. [root@localhost ~]# exit
8. logout
9. Connection to 192.168.1.130 closed.

linux下匹配 #,windows下匹配 >

远程登录到ftp,并且下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. \#!/usr/bin/expect -f 
2. set ip [lindex $argv 0 ]
3. set dir [lindex $argv 1 ]
4. set file [lindex $argv 2 ]
5. set timeout 10
6. spawn ftp $ip
7. expect "Name*"
8. send "zwh\r"
9. expect "Password:*"
10. send "zwh\r"
11. expect "ftp>*"
12. send "lcd $dir\r"
13. expect {
14. "*file" { send_user "local $_dir No such file or directory";send "quit\r" }
15. "*now*" { send "get $dir/$file $dir/$file\r"}
16. }
17. expect {
18. "*Failed" { send_user "remote $file No such file";send "quit\r" }
19. "*OK" { send_user "$file has been download\r";send "quit\r"}
20. }
21. expect eof

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. root@nmk# ./test2.exp 192.168.1.130 /var/www/www aaa.html 
2. spawn ftp 192.168.1.130
3. Connected to 192.168.1.130.
4. 220 (vsFTPd 2.0.5)
5. Name (192.168.1.130:root): zwh
6. 331 Please specify the password.
7. Password:
8. 230 Login successful.
9. Remote system type is UNIX.
10. Using binary mode to transfer files.
11. ftp> lcd /var/www/www
12. Local directory now /var/www/www
13. ftp> get /var/www/www/aaa.html /var/www/www/aaa.html
14. local: /var/www/www/aaa.html remote: /var/www/www/aaa.html
15. 200 PORT command successful. Consider using PASV.
16. 150 Opening BINARY mode data connection for /var/www/www/aaa.html (66 bytes).
17. 226 File send OK.
18. 66 bytes received in 0.00 secs (515.6 kB/s)
19. quit aaa.html has been download
20. 221 Goodbye.

单台服务器scp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/expect 
if {$argc < 2} {
send_user "usage: $argv0 src_file username ip dest_file password\n"
exit
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]
spawn scp $src_file $username@$host:$dest_file
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "100%"
expect eof

说明:

(1)注意代码刚开始的第一行,指定了expect的路径,与shell脚本相同,这一句指定了程序在执行时到哪里去寻找相应的启动程序。代码刚开始还设定了timeout的时间为10秒,如果在执行scp任务时遇到了代码中没有指定的异常,则在等待10秒后该脚本的执行会自动终止。

(2)这个脚本设置了5个需要手动输入的参数,分别为:目标主机的IP、用户名、密码、本地文件路径、目标主机中的文件路径。如果将以上脚本保存为expect_scp文件,则在shell下执行时需要按以下的规范来输入命令:

1
./expect_scp 192.168.75.130 root 123456 /root/src_file /root/dest_file

以上的命令执行后,将把本地/root目录下的src_file文件拷贝到用户名为root,密码为123456的主机192.168.75.130中的/root下,同时还将这个源文件重命名为dest_file。

(3)spawn代表在本地终端执行的语句,在该语句开始执行后,expect开始捕获终端的输出信息,然后做出对应的操作。expect代码中的捕获的(yes/no)内容用于完成第一次访问目标主机时保存密钥的操作。有了这一句,scp的任务减少了中断的情况。代码结尾的expect eof与spawn对应,表示捕获终端输出信息的终止。

多台传输脚本

1
cat mainscp.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
host_list="server_list.conf"
cat $host_list | while read line
do
host_ip=`echo $line|awk '{print $1}'`
username=`echo $line|awk '{print $2}'`
password=`echo $line|awk '{print $3}'`
src_file=`echo $line|awk '{print $4}'`
dest_file=`echo $line|awk '{print $5}'`
##key=`echo $line|awk '{print $6}'`
##./allscp.sh $key $src_file $username $host_ip $dest_file $password
./allscp.sh $src_file $username $host_ip $dest_file $password
done

3.服务器信息文件

1
cat server_list.conf

格式为:

1
ip 用户名 密码 源文件 目标文件地址

通过jenkins加expect部署新项目或增加节点

update.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/expect

if {$argc < 3} {
send_user "请按照格式输入: $argv0 host_ip username password local_file remote_file project port\n"
exit
}

#接收输入的参数定义为变量
set host_ip [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set local_file [lindex $argv 3]
set remote_file [lindex $argv 4]
set project [lindex $argv 5]
set port [lindex $argv 6]

spawn scp package/$project.war $username@$host_ip:$remote_file

expect {
"*password:"
{send "$password\r"}
}
expect "100%"
expect eof
spawn ssh $username@$host_ip

expect {
"(yes/no)?"
{send "yes\r"}
"*password:"
{send "$password\r"}
}
expect "$"
send "cd $remote_file\r"
expect "$"
send "./killport.sh $port\r"
expect "$"
send "cd /home/tomcat/tomcat_$port/webapps\r"
expect "$"
send "tar czvf $project`date +%Y%m%d%H%M%S`.bak.tar $project\r"
expect "$"
send "rm -r $project.war\r"
expect "(yes/no)?"
send "yes\r"
send "rm -rf $project\r"
expect "$"
send "mv *.bak.tar backup\r"
expect "$"
send "mv $remote_file/$project.war /home/tomcat/tomcat_$port/webapps\r"
expect "$"
send "cd /home/tomcat/tomcat_$port/bin\r"
expect "$"
send "./startup.sh\r"
expect eof

Release.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/bin/bash

project=$2
host_ip=$4
port=$6

line1=`ls package|grep $project`
if [ "$project.war" != "$line1" ] ; then
echo -e "请先去jenkins打包,不存在$project的war包.\n请检查拼写格式:\n
-P(project name)\n
-H(host ip)\n
-Port(tomcat port)\n
例如:./release.sh -P mfapi -H 192.168.1.1 -Port 8080"
exit
fi

line2=`cat files/server_list.conf|grep $host_ip|awk '{print $1}'`
if [ "$host_ip" != "$line2" ] ; then
echo -e "服务器列表不存在$host_ip.\n请检查拼写格式:\n
-P(project name)\n
-H(host ip)\n
-Port(tomcat port)\n
例如:./release.sh -P mfapi -H 192.168.1.1 -Port 8080"
exit
fi

#line3=`ls tomcat/conf|grep $port`
if [ ! -n "$port" ] ; then
echo -e "tomcat中不存在端口为$port的配置,如有需要请添加.\n请检查拼写格式:\n
-P(project name)\n
-H(host ip)\n
-Port(tomcat port)\n
例如:./release.sh -P mfapi -H 192.168.1.1 -Port 8080"
exit
fi

if [ "$1"a == "-P"a ] && [ "$3"c == "-H"c ] && [ "$5"e == "-Port"e ] ; then
line=`cat files/server_list.conf |grep $host_ip`
ip=`echo $line|awk '{print $1}'`
username=`echo $line|awk '{print $2}'`
password=`echo $line|awk '{print $3}'`
local_file=`echo $line|awk '{print $4}'`
remote_file=`echo $line|awk '{print $5}'`
./deploy.sh $host_ip $username $password $local_file $remote_file $project $port
else
echo -e "请检查拼写格式:\n
-P(project name)\n
-H(host ip)\n
-Port(tomcat port)\n
例如:./release.sh -P mfapi -H 192.168.1.1 -Port 8080"
exit
fi

Deploy.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/usr/bin/expect

if {$argc < 3} {
send_user "请按照格式输入: $argv0 host_ip username password local_file remote_file project port\n"
exit
}

#接收输入的参数定义为变量
set host_ip [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set local_file [lindex $argv 3]
set remote_file [lindex $argv 4]
set project [lindex $argv 5]
set port [lindex $argv 6]

spawn cp files/tomcat.tar.gz .
expect eof
spawn tar zvxf tomcat.tar.gz
expect eof

#判断项目---------------------------
if { "$project" != "" } {
spawn cp /home/jenkins/nmk/package/$project.war /home/jenkins/nmk/tomcat/webapps/
expect eof
} else {
spawn echo "请输入项目名"
exit
}

#-----------------------------------

#判断端口---------------------------

if { "$port" != "" } {
spawn rm -r /home/jenkins/nmk/tomcat/conf/server.xml
spawn cp /home/jenkins/nmk/tomcat/conf/server_$port.xml /home/jenkins/nmk/tomcat/conf/server.xml
expect eof
} else {
spawn echo "请输入端口"
exit
}

#-----------------------------------
spawn rm -r tomcat.tar.gz
expect eof
spawn mv tomcat tomcat_$port
expect eof
spawn tar zcvf tomcat_$port.tar.gz tomcat_$port
expect eof
spawn scp $local_file/tomcat_$port.tar.gz $username@$host_ip:$remote_file

expect {
"(yes/no)?"
{send "yes\r"}
"*password:"
{send "$password\r"}
}
expect "100%"
expect eof
spawn rm -r tomcat_$port.tar.gz
expect eof
spawn rm -r tomcat_$port
expect eof
spawn ssh $username@$host_ip

expect {
"(yes/no)?"
{send "yes\r"}
"*password:"
{send "$password\r"}
}
expect "$"
send "cd $remote_file\r"
expect "$"
send "tar zvxf tomcat_$port.tar.gz\r"
expect "$"
send "rm -r tomcat_$port.tar.gz\r"
expect "(yes/no)?"
send "yes\r"
expect "$"
send "cd tomcat_$port\r"
expect "$"
send "./bin/startup.sh\r"
expect eof

Files/server_list.conf

1
Hostile username password srcfile distfile

更多expect的详细命令

https://www.cnblogs.com/lixigang/articles/4849527.html