自动应答工具expect的使用
Expect 是用于自动化交互式应用程序
1. 软件介绍
现代的 Shell 对程序提供了最小限度的控制(程序的开始/停止/关闭等),而把交互的特性留给了用户。这意味着有些程序,你不能非交互的运行,比如说 passwd 命令。
Expect 恰恰填补了其中的一些裂痕,解决了在 Unix 环境中长期存在着的一些问题。
Expect 使用 Tcl 作为语言核心,不管程序是交互和还是非交互的,Expect 都能运用。
2. 工具安装
因为Expect需要Tcl编程语言的支持
| $ sudo yum install -y gcc
$ sudo yum install -y tcl tclx tcl-devel
$ sudo yum install expect
# ubuntu
$ sudo apt install expect
|
3. 基础知识
主要介绍常见的 4 个命令的使用方式
| 编号 |
命令 |
作用 |
| 1 |
send |
send 命令接收一个字符串并将该参数发送到进程中 |
| 2 |
expect |
expect 通常用来等待进程的反馈再发送对应的交互命令 |
| 3 |
spawn |
spawn 命令用来启动新的进程 |
| 4 |
interact |
允许退出自动化进入人工交互 |
4. 控制结构
介绍 TCL 语言的控制结构
if else
| #!/usr/bin/expect
set timeout 10
set alias_host [lindex $argv 0]
set b1_password ASJZOMxlgM^9
set b2_password a0yDuePSLUGM
if {$argc!=1} {
echo "请输入想要远程连接的服务器: [b1|b2]"
exit 1
}
if {$alias_host=="b1"} {
spawn ssh [[emailprotected]](/cdn-cgi/l/email-protection) -p 22
expect "*password*" {send "$b1_password\r"}
interact
} elseif {$alias_host=="b2"} {
spawn ssh [[emailprotected]](/cdn-cgi/l/email-protection) -p 22
expect "*password*" {send "$b2_password\r"}
interact
} else {
send "请输入想要远程连接的服务器: [b1|b2]"
}
|
switch
| #!/usr/bin/expect
set timeout 10
set alias_host [lindex $argv 0]
set b1_password ASJZOMxlgM^9
set b2_password a0yDuePSLUGM
switch -glob -- $file1 {
b1 {
spawn ssh [[emailprotected]](/cdn-cgi/l/email-protection) -p 22
expect "*password*" {send "$b1_password\r"}
interact
}
b2 {
spawn ssh [[emailprotected]](/cdn-cgi/l/email-protection) -p 22
expect "*password*" {send "$b2_password\r"}
interact
}
|
while
| #!/usr/bin/expect
set test 0
while {$test<10} {
set test [expr {$test + 1}]
if {$test > 7}
break
if "$test < 3"
continue
}
|
catch
| #!/usr/bin/expect
proc Error {} {
error "This is a error for test"
}
catch Error test
puts $test
|
5. 简单使用
系统指定修改用户密码
| #!/usr/bin/expect -d
set timeout 30
spawn passwd [lindex $argv 1]
set password [lindex $argv 2]
expect "*New password:*" {send "$password\r"}
expect "*Retype new password:*" {send "$password\r"}
expect eof
|
登陆远程服务器并停留在远程服务器上
| #!/usr/bin/expect
set timeout 30 # 设置超时时间
set host "100.200.200.200"
set username "root"
set password "123456"
# 给ssh运行进程加个壳用来传递交互指令
spawn ssh $username@$host
# 判断上次输出结果里是否包含指定的字符串
expect {
# exp_continue表示继续执行下一步
"*yes/no" {send "yes\r";exp_continue}
# 匹配即可发送密码到对应进程中
"*password*" {send "$password\r"}
}
# 执行完成后保持交互状态
interact
|
传输参数执行登并停留在远程服务器上
| # login.exp
#!/usr/tcl/bin/expect
# $argc表示参数个数
if {$argc < 3} {
puts "Usage:cmd <host>:<username> -p <port>"
exit 1
}
# 获取第几个参数的内容
set timeout 30
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set port [lindex $argv 3]
spawn ssh $username@$host -p $port
expect "*password*" {send "$password\r"}
interact
|
在 shell 脚本中使用 expect
| # [1] 直接添加expect脚本文件
#!/bin/bash
read -p "please input you user:" -t30 remote_user
read -p "please input you ip:" -t30 remote_ip
read -p "please input you port:" -t30 remote_port
echo "ssh $remote_user:$remote_ip -p $remote_port"
./login.exp $remote_user $remote_ip $remote_port
|
| # [2] 在shell脚本直接写入expect命令
#!/bin/bash
read -p "please input you user:" -t30 remote_user
read -p "please input you ip:" -t30 remote_ip
read -p "please input you port:" -t30 remote_port
echo "ssh $remote_user:$remote_ip -p $remote_port"
expect -d <<EOF
spawn ssh $remote_ip
expect {
"*yes/no" {send "yes\r";exp_continue}
"*password:" {send "Xuansiwei123!\r"}
}
exit
expect eof;
EOF
|
6. 高级示例
弄懂下面的高级玩法,就可以应对日常的工作使用了
自动 telnet 会话
| #!/usr/bin/expect -f
set ip [lindex $argv 0 ] # 接收第1个参数,作为IP
set userid [lindex $argv 1 ] # 接收第2个参数,作为userid
set mypassword [lindex $argv 2 ] # 接收第3个参数,作为密码
set mycommand [lindex $argv 3 ] # 接收第4个参数,作为命令
set timeout 10 # 设置超时时间
# 向远程服务器请求打开一个telnet会话,并等待服务器询问用户名
spawn telnet $ip
expect "username:"
# 输入用户名,并等待服务器询问密码
send "$userid\r"
expect "password:"
# 输入密码,并等待键入需要运行的命令
send "$mypassword\r"
expect "%"
# 输入预先定好的密码,等待运行结果
send "$mycommand\r"
expect "%"
# 将运行结果存入到变量中,显示出来或者写到磁盘中
set results $expect_out(buffer)
# 退出telnet会话,等待服务器的退出提示EOF
send "exit\r"
expect eof
|
自动建立 FTP 会话
| #!/usr/bin/expect -f
set ip [lindex $argv 0 ] # 接收第1个参数,作为IP
set userid [lindex $argv 1 ] # 接收第2个参数,作为Userid
set mypassword [lindex $argv 2 ] # 接收第3个参数,作为密码
set timeout 10 # 设置超时时间
# 向远程服务器请求打开一个FTP会话,并等待服务器询问用户名
spawn ftp $ip
expect "username:"
# 输入用户名,并等待服务器询问密码
send "$userid\r"
expect "password:"
# 输入密码,并等待FTP提示符的出现
send "$mypassword\r"
expect "ftp>"
# 切换到二进制模式,并等待FTP提示符的出现
send "bin\r"
expect "ftp>"
# 关闭ftp的提示符
send "prompt\r"
expect "ftp>"
# 下载所有文件
send "mget *\r"
expect "ftp>"
# 退出此次ftp会话,并等待服务器的退出提示EOF
send "bye\r"
expect eof
|
自动登录 ssh
| #!/usr/bin/expect -f
set ip [lindex $argv 0 ] # 接收第1个参数,作为IP
set username [lindex $argv 1 ] # 接收第2个参数,作为username
set mypassword [lindex $argv 2 ] # 接收第3个参数,作为密码
set timeout 10 # 设置超时时间
spawn ssh $username@$ip # 发送ssh请求
expect { # 返回信息匹配
"*yes/no" { send "yes\r"; exp_continue} # 第一次ssh连接会提示yes/no,继续
"*password:" { send "$mypassword\r" } # 出现密码提示,发送密码
}
interact # 交互模式,用户会停留在远程服务器上面
|
自动登录 ssh 执行命令
| #!/usr/bin/expect
set IP [lindex $argv 0]
set USER [lindex $argv 1]
set PASSWD [lindex $argv 2]
set CMD [lindex $argv 3]
spawn ssh $USER@$IP $CMD
expect {
"(yes/no)?" {
send "yes\r"
expect "password:"
send "$PASSWD\r"
}
"password:" {send "$PASSWD\r"}
"* to host" {exit 1}
}
expect eof
|
批量登录 ssh 服务器执行操作范例 => for 循环
| #!/usr/bin/expect
for {set i 10} {$i <= 12} {incr i} {
set timeout 30
set ssh_user [lindex $argv 0]
spawn ssh -i .ssh/$ssh_user abc$i.com
expect_before "no)?" {
send "yes\r" }
sleep 1
expect "password*"
send "hello\r"
expect "*#"
send "echo hello expect! > /tmp/expect.txt\r"
expect "*#"
send "echo\r"
}
exit
|
批量登录 ssh 并执行命令 => foreach 语法
| #!/usr/bin/expect
if {$argc!=2} {
send_user "usage: ./expect ssh_user password\n"
exit
}
foreach i {11 12} {
set timeout 30
set ssh_user [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh -i .ssh/$ssh_user [[emailprotected]](/cdn-cgi/l/email-protection)
expect_before "no)?" {
send "yes\r" }
sleep 1
expect "Enter passphrase for key*"
send "password\r"
expect "*#"
send "echo hello expect! > /tmp/expect.txt\r"
expect "*#"
send "echo\r"
}
exit
|
批量 ssh 执行命令 => 用 shell 调用 tclsh 方式、多进程同时执行
| #!/bin/sh
exec tclsh $0 "$@"
package require Expect
set username [lindex $argv 0]
set password [lindex $argv 1]
set argv [lrange $argv 2 end]
set prompt "(%|#|\\$) $"
foreach ip $argv {
spawn ssh -t $username@$ip sh
lappend ids $spawn_id
}
expect_before -i ids eof {
set index [lsearch $ids $expect_out(spawn_id)]
set ids [lreplace $ids $index $index]
if [llength $ids] exp_continue
}
expect -i ids "(yes/no)\\?" {
send -i $expect_out(spawn_id) yes\r
exp_continue
} -i ids "Enter passphrase for key" {
send -i $expect_out(spawn_id) \r
exp_continue
} -i ids "assword:" {
send -i $expect_out(spawn_id) $password\r
exp_continue
} -i ids -re $prompt {
set spawn_id $expect_out(spawn_id)
send "echo hello; exit\r"
exp_continue
} timeout {
exit 1
}
|
使用 ssh 自动登录 expect 脚本 => ssh.expect
| The authenticity of host '192.168.xxx.xxx (192.168.xxx.xxx)' can't be established.
RSA key fingerprint is 25:e8:4c:89:a3:b2:06:ee:de:66:c7:7e:1b:fa:1c:c5.
Are you sure you want to continue connecting (yes/no)?
Warning: Permanently added '192.168.xxx.xxx' (RSA) to the list of known hosts.
Enter passphrase for key '/data/key/my_dsa':
Last login: Sun Jan 26 13:39:37 2019 from 192.168.xxx.xxx
[root@master003 ~]#
[[emailprotected]](/cdn-cgi/l/email-protection)'s password:
Last login: Thu Jan 23 17:50:43 2019 from 192.168.xxx.xxx
|
| #!/usr/bin/expect -f
if {$argc < 4} {
send_user "Usage:\n $argv0 IPaddr User Passwd Port Passphrase\n"
puts stderr "argv error!\n"
sleep 1
exit 1
}
set timeout 30
set ip [lindex $argv 0 ]
set user [lindex $argv 1 ]
set passwd [lindex $argv 2 ]
set port [lindex $argv 3 ]
set passphrase [lindex $argv 4 ]
if {$port == ""} {
set port 22
}
spawn ssh $user@$ip -p $port
expect_before "(yes/no)\\?" {
send "yes\r"}
expect \
"Enter passphrase for key*" {
send "$passphrase\r"
exp_continue
} " password:?" {
send "$passwd\r"
exp_continue
} "*\[#\\\$]" {
interact
} "* to host" {
send_user "Connect faild!"
exit 2
} timeout {
send_user "Connect timeout!"
exit 2
} eof {
send_user "Lost connect!"
exit
}
|
7. 参考博客
expect - 教程中文版
expect - 正则模式说明
expect - 自动交互脚本