Linux shell 脚本通过expect实现自动输入密码
一、expect 简介
expect 是一个自动交互功能的工具,可以实现自动登录,不必手动输入密码(Password)、确认(Yes)等交互操作。
expect 是开了一个子进程,通过spawn来执行shell脚本,except监测到脚本的返回结果,并发送交互输入内容(send)
二、except 安装
1. Ubuntu 安装
sudo apt-get -y install tcl expect
2. CentOS 安装
yum -y install expect
3. MacOS 安装
brew install expect
安装完成后的目录为
# which expect
/usr/bin/expect
三、except 使用
1. expect 使用示例
#!/usr/bin/expect # # mimvp.com # 2016.10.12 set timeout 30 set passwd "mimvp.com" spawn ssh -l mimvp 123.57.78.100 expect "password:" send "$passwd\n" interact
说明:
#!/usr/bin/expect 表示使用expect的shell交互模式,由expect来解释执行的而不是由bash,所以代码的语法和shell脚本也是不一样的
set timeout 30 表示对变量timeout赋值,为30秒。如果执行的shell命令耗时长可以设置超时时间长一些,默认timeout为10秒
set passwd "mimvp.com" 表示对变量passwd赋值,密码供后面的引用。特殊字符需要转义,例如 "\$mimvp.com"
spawn ssh -l mimvp 123.57.78.100 表示在expect下执行shell脚本,例如本例的 ssh 远程登录服务器
expect "password:" 表示对执行shell脚本的返回字符串进行判断,是否包含"password:"字段,一般可写做 "*assword:"
send "$passwd\n" 表示如果expect监测到了包含的字符串,将输入send中的内容,\n相当于回车,有的也写做 \r
interact 表示留在新开的子进程内,可以继续输入,否则将退出子进程回到shell中。例如 ssh登录到某台服务器上,只有加了interact才可以留在登录后的机器上进行操作
2. expect 命令行参数
[lindex $argv n]获得参数下标为 index = n 的参数(index从0开始计算)
$argc为命令行参数的个数
[lrange $argv 0 0]表示第一个参数
[lrange $argv 0 3]表示第1到第3个参数
用法示例:
#!/usr/bin/expect # # mimvp.com # 2016.10.12 set timeout 30 set passwd "mimvp.com" set ipaddr [lrange $argv 0 0] #set ipaddr [lindex $argv 0] spawn ssh -l root $ipaddr expect "password:" send "$passwd\n" interact ## ./conn_server.sh 123.57.78.100
上例里,执行语句为
./conn_server.sh 123.57.78.100
其中:
./conn_server.sh 是脚本文件名
123.57.78.100 是服务器IP地址,set ipaddr [lrange $argv 0 0] 获取第一个参数IP地址,并赋值给 ipaddr
spawn ssh -l root $ipaddr 表示使用IP地址,进行ssh登录
例如:scp_service.sh文件,可以 ./scp_service.sh -rm 来执行,这时 -rm 是赋值的第一个参数
set option [lindex $argv 0](获得第一个参数 -rm 存到变量option中,参数是的index是从0开始计算的)
3. expect 判断语句
expect支持if语句 if...elif...else...
if { 条件1 } { 条件1执行语句 } elif { 条件2 } { 条件2执行语句 } else { 其他情况执行语句 }
用法示例:
#!/usr/bin/expect # # mimvp.com # 2016.10.12 set timeout 30 set passwd "mimvp.com" if { [llength $argv] < 2 } { puts "Usage:" puts "$argv0 username ipaddr" exit 1 } #set username [lindex $argv 0] #set ipaddr [lindex $argv 1] set username [lrange $argv 0 0] set ipaddr [lrange $argv 1 1] spawn ssh -l $username $ipaddr expect "password:" send "$passwd\n" interact $ ./conn_server.sh mimvp ## error Usage: ./conn_server.sh username ipaddr $ ./conn_server.sh mimvp 123.57.78.100 ## success
说明:
1. if 的条件用{}来包含条件
2. if 和 后面的{}必须有空格隔开
3. 两个花括号之间必须有空格隔开,比如if {} {},否则会报错 expect:extra characters after close-brace
3. 使用{来衔接下一行,所以if的条件后需要加左花括号{
4. else不能单独放一行,所以else要跟在}后面
except 实现 scp 拷贝
#!/usr/bin/expect # # mimvp.com # 2016.10.12 set timeout 30 set passwd "mimvp.com" if { [llength $argv] < 2 } { puts "Usage:" puts "$argv0 local_file remote_path" exit 1 } set local_file [lindex $argv 0] set remote_file [lindex $argv 1] set passwd_error 0 spawn scp $local_file $remote_file expect { "*assword:*" { if { $passwd_error == 1 } { puts "passwd is error" exit 2 } set timeout 100 set passwd_error 1 send "$passwd\n" exp_continue } "*es/no)?*" { send "yes\n" exp_continue } timeout { puts "connect is timeout" exit 3 } }
4. expect {} 多行期望
有时执行shell后预期结果是不固定的,有可能是询问是yes/no,有可能是去输入密码,所以可以用expect{}(比如sudo命令,第一次使用sudo时需要输入密码,但是它有5分钟的有效时间,5分钟内是不需要再去输入的)
花括号内放多行语句,从上至下匹配,匹配到哪个expect,则执行哪句。
这里如果匹配到第一行会执行第一行;然后第一行的执行结果如果匹配到第二行也会执行第三行;
如果某一行没有匹配到会向下寻找匹配到的那一行进行执行)
注意:多行的expect的{后不要跟语句,否则读不到这条,需要换行后去写具体的期望值和操作。
#!/usr/bin/expect # # mimvp.com # 2016.10.12 expect { "*assword" {send "mimvp.com\n"} "*es/no)?*" {send "yes\n"} "exit" {send "exit\n"} exp_continue }
说明:exp_continue表示继续执行下面的expect。
四、shell中调用expect实现自动登录
通过在shell脚本中,执行expect脚本的方式来实现的。
当然可以将shell中定义的一些变量传递给expect脚本作为参数输入。
示例1:shell 调用 expect 脚本,间接调用
#!/bin/bash # # mimvp.com # 2016.10.12 script_tmp="/Users/homer/script_tmp" if [ ! -d ${script_tmp} ]; then echo "${script_tmp} is not found" mkdir -p ${script_tmp} cd ${script_tmp} else cd ${script_tmp} fi ## expect file #./scp_remote_server.sh /Users/homer/script/scp_remote_server.sh
示例2:shell 嵌套 expect 脚本,直接调用(推荐)
#!/bin/bash # # mimvp.com # 2016.10.12 username='mimvp-guest' passwd='mimvp.com' passwd2='\$mimvp.com' /usr/bin/expect <<-EOF set timeout 30 spawn /usr/bin/htpasswd -c /etc/squid/passwd $username ## first password expect { "*es/no:" { send "yes\n"; exp_continue } "*assword:" { send "$passwd\n; exp_continue" } } ## second password to confirm expect "*assword:" send "$passwd\n" spawn ssh mimvp@123.57.78.100 expect { "*es/no" { send "yes\r"; exp_continue } "*assword" { send "$passwd2\r" } } expect "*#" send "cd /home/script/\r" #expect "*#" #send "svn up\r" expect "*#" send "ls -l\r" expect "*#" send "exit\r" #interact expect eof EOF
本段脚本,实现了两个功能:
功能1,使用htpasswd命令,输入密码,并再次确认密码
功能2,远程登录服务器,并svn up下载代码,并退出远程服务器
以上脚本,君测试成功!
说明:经过这次尝试些expect,给我的感觉是expect对格式的要求比较高,比如花括号之间必须有空格啊之类的,所以如果有报错,大家可以仔细观察一下是不是语法格式错误了。
expect 更多语法
1. expect中的判断语句
if { condition } { # do your things } elseif { # do your things } else { # do your things }
expect中没有小括号(),所有的if/else, while, for的条件全部使用大括号{}, 并且{ 与左边要有空格,否则会报错。另,else 不能单独占一行,否则会报错。
2. 字符串比较
if { "$node" == "apple" } { puts "apple" } elseif { "$node" == "other" } { puts "invalid name" exit 70 } else { puts "asd" }
对比string,使用==表示相等, !=标示不相等。
3. switch 语句
switch $location { "apple" { puts "apple" } "banana" { puts "banana" } default { puts "other" }
记得左大括号{ 的左边要有空格,否则会报错
4. 读取用户输入
expect_user -re "(.*)\n"
send_user "$expect_out(1, string)\n"
expect_user -re 表示正则表达式匹配用户按下回车前输入的所有字符
expect_out(1, string) 表示第一个匹配的内容,即回车前所有字符
expect_out(buffer) 所有的buffer内容
5. break && continue
如c中一样,expect一样可以使用break && continue, 并且功能相同。注:只能用在循环中。
6. 定义交互命令
# stick control + z in variable set ControlZ \032 # stick control + c in variable set ControlC \x03 # define string embedded ctrl-z && tab set oddword foo\032bar\tgorp
五、在远程服务器上配置ssh信任
网上有很多教程,推荐米扑博客:Linux两台主机之间建立信任 (非常经典,验证成功)
感觉长期的话应该比写expect方便,但是我觉得写脚本的话还是最好不要总去操作其他地方,
所以这里我就用expect自己来写的(当然也是想练习一下写expect)
参考推荐:
Linux 修改默认端口、增加普通用户、使用密钥等安全登录SSH
Linux之/etc/profile、~/.bash_profile等几个文件的执行过程
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2019-11-20 08:54:17
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!