使用expect实现自动化任务

expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。
expect自动交互流程:
spawn启动指定进程–>expect获取指定关键字–>send向指定程序发送指定字符–>执行完成退出.

expect命令参数

1
2
3
4
5
6
7
8
9
10
spawn               交互程序开始后面跟命令或者指定程序
expect 获取匹配信息匹配成功则执行expect后面的程序动作
send exp_send 用于发送指定的字符串信息,用于模拟用户输入
exp_continue 在expect中多次匹配就需要用到
send_user 用来打印输出 相当于shell中的echo
exit 退出expect脚本
eof expect执行结束 退出
set 定义变量
puts 输出变量
set timeout 设置超时时间

ssh登录远程主机执行命令

1
2
3
4
5
#!/usr/bin/expect
spawn ssh root@192.168.59.131 #模拟用户输入ssh root@192.168.59.131以连接主机
expect "password:" #expect从spawn进程接收字符串
send "12345\r" #当上一步的expect接收到指定信息后才会发送12345字符串信息,模拟用户输入密码并\r回车
interact

scp传输文件

expect使用多分支语法时,会进行依次匹配循环,当出现报错时使用exit 1退出循环,eof是在等待结束标志。由spawn启动的命令在结束时会产生一个eof标记,由于scp传输文件过大会导致传输时间过长,所以将timeout 设置为-1即可无限制的等待。

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
#!/usr/bin/expect -f 


set timeout -1
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set sshport [lindex $argv 3]
set src_file [lindex $argv 4]
set dest_file [lindex $argv 5]

spawn scp -P $sshport -r $src_file $username@$host:$dest_file

expect {

"*yes/no*" {
send "yes\r"
exp_continue
}

"*assword:*" {
send "$password\r"
exp_continue
}

"Permission denied" {
send_user "错误:权限被拒绝\n"
exit 1
}

timeout {
send_user "错误:连接超时\n"
exit 1


}
"100%"
{
send_user "传输成功\n"

}
eof


}

ftp上传文件

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
#!/usr/bin/expect -f 


set timeout -1
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set ftpport [lindex $argv 3]
set src_file [lindex $argv 4]
set dest_dir [lindex $argv 5]


spawn sftp -o Port=$ftpport $username@$host

expect {

"*yes/no*" {
send "yes\r"
exp_continue
}

"*assword:*" {
send "$password\r"
exp_continue
}

timeout {
send_user "错误:连接超时\n"
exit 1


}



}



expect "sftp>"
send "cd $dest_dir\r"

expect "sftp>"
send "put $src_file\r"

expect "sftp>"
send "bye\r"

expect eof

将expect封装到shell函数中

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
function remote_upload_file() {
local local_file="$1"
local remote_dir="$2"
local filename="$3"
local host="192.168.59.131"
local user="admin"
local pass="admin"
local port="21"

expect -c "

set timeout -1

send_user \"开始连接到FTP服务器: $host\n\"


spawn ftp $host $port

expect {
\"Name*\" {
send \"$user\r\"
exp_continue
}
\"Password:\" {
send \"$pass\r\"
}
\"Login failed\" {
send_user \"FTP登录失败\n\"
exit 1
}
\"Not connected\" {
send_user \"无法连接到FTP服务器\n\"
exit 1
}
timeout {
send_user \"连接超时\n\"
exit 1
}
}


expect {
\"ftp>\" {
send_user \"成功登录FTP服务器\n\"
}
\"Login incorrect\" {
send_user \"用户名或密码错误\n\"
exit 1
}
timeout {
send_user \"登录响应超时\n\"
exit 1
}
}


send \"cd $remote_dir\r\"
expect {
\"ftp>\" {
send_user \"成功切换到远程目录:$remote_dir\n\"
}
\"No such file or directory\" {
send_user \"远程目录不存在: $remote_dir\n\"
send \"bye\r\"
expect eof
exit 1
}
timeout {
send_user \"切换目录超时\n\"
exit 1
}
}


send \"binary\r\"
expect \"ftp>\"



send_user \"开始上传文件: $local_file -> $remote_dir/$filename\n\"


send \"put $local_file $filename\r\"

expect {
\"ftp>\" {
send_user \"文件上传成功完成\n\"
}
\"Transfer complete\" {
expect \"ftp>\"
send_user \"文件上传成功完成\n\"
}
\"No such file or directory\" {
send_user \"错误: 无法访问本地文件\n\"
exit 1
}
\"Connection closed\" {
send_user \"错误: 连接意外关闭\n\"
exit 1
}
timeout {
send_user \"错误: 上传超时\n\"
exit 1
}
}

send \"bye\r\"
expect eof
send_user \"FTP会话结束\n\"
"
function upload_file_ftp() {

local file=$(find /data -type f -atime -1 -name "*.gz") #find找到需要上传的文件路径
local filename=$(echo $file|awk -F'/' '{print $NF}') #将获取的文件路径按/切割文件名

local dir="ftp/"
echo "$(date +"%Y-%m-%d %H:%M:%S") 开始上传文件: $filename"

remote_upload_file "$file" "$dir" "$filename"
if [ $? -eq 0 ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") 上传成功"
else
echo "$(date +"%Y-%m-%d %H:%M:%S") 上传失败"
fi
}

expect send可能会出现invalid command name或 no such variable 没此变量的报错
将特殊符号进行转义,即[如语句里面特殊符号太多,转义太麻烦。

可以使用send – {}格式,将send的内容放到大括号中转义。

1
2
3
4
5
send "awk 'BEGIN{FS=[ ,]} { print $2+$4 }' file\r "
替换为
send -- {awk 'BEGIN{FS=[ ,]} { print $2+$4 }' file}
send -- \r