一、expect初探
expect是一个免费的编程工具语言;expect是在Linux系统上的一个自动化交互套件,主要应用于执行命令和程序,系统以交互形式要求输入指定字符串,实现交互通信。shell和expect理解为两种不同的脚本语言,expect有独自的语法、变量
二、expect自动交互流程
1、spawn启动指定进程---expect获取指定关键字---send向指定程序发送指定字符---执行完成退出.
2、注意该脚本能够执行的前提是安装了expect
3、expect常用命令总结:
lspawn 交互程序开始后面跟命令或者指定程序
lexpect 获取匹配信息匹配成功则执行expect后面的程序动作
lsend exp_send 用于发送指定的字符串信息
lexp_continue 在expect中多次匹配就需要用到
lsend_user 用来打印输出 相当于shell中的echo
lexit 退出expect脚本
leof expect执行结束 退出
lset 定义变量
lputs 输出变量
lset timeout 设置超时时间
三.脚本案例
1、ssh登录远程主机执行命令,执行方法 expect 1.sh 或者 ./1.sh
# vim 1.sh
#!/usr/bin/expect
spawn ssh saneri@192.168.21.13 df -Th
expect "*password"
send "yc1256\n"
expect eof
2、ssh远程登录主机执行命令,在shell脚本中执行expect命令,执行方法sh 2.sh、bash 2.sh 或./2.sh都可以执行.
#!/bin/bash
passwd='123456'
/usr/bin/expect <<-eof
set time 30
spawn ssh saneri@192.168.56.103 df -Th
expect {
"*yes/no" { send "yes\r"; exp_continue }
"*password:" { send "$passwd\r" }
}
expect eof
EOF
3.expect执行多条命令
#!/usr/bin/expect -f
set timeout 10
spawn sudo su - root
expect "*password*"
send "123456\r"
expect "#*"
send "ls\r"
expect "#*"
send "df -Th\r"
send "exit\r"
expect eof
4. 创建ssh key,将id_rsa和id_rsa.pub文件分发到各台主机上面。
4.1、创建主机配置文件
[root@localhost script]# cat host
192.168.1.10 root 123456
192.168.1.20 root 123456
192.168.1.30 root 123456
[root@localhost script]# ls
copykey.sh hosts
4.2、编写copykey.sh脚本,自动生成密钥并分发key.
[root@localhost script]# vim copykey.sh
#!/bin/bash
# 判断id_rsa密钥文件是否存在
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
else
echo "id_rsa has created ..."
fi
#分发到各个节点,这里分发到host文件中的主机中.
while read line
do
user=`echo $line | cut -d " " -f 2`
ip=`echo $line | cut -d " " -f 1`
passwd=`echo $line | cut -d " " -f 3`
expect <
set timeout 10
spawn ssh-copy-id $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$passwd\n" }
}
expect "password" { send "$passwd\n" }
EOF
done
5、shell调用expect执行多行命令.
#!/bin/bash
ip=$1
user=$2
password=$3
expect <
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "touch tmp/test.txt\n" }
expect "]#" { send "exit\n" } expect eof
EOF
#./ssh5.sh 192.168.1.10 root 123456
6、使用普通用户登录远程主机,并通过sudo到root权限,通过for循环批量在远程主机执行命令.
$ cat timeout_login.txt
10.0.1.8
10.0.1.34
10.0.1.88
10.0.1.76
10.0.1.2
10.0.1.3
#!/bin/bash
for i in `cat home/admin/timeout_login.txt`
do
usr/bin/expect <
spawn usr/bin/ssh -t -p 22022 admin@$i "sudo su -"
expect {
"yes/no" { send "yes\r" }
}
expect {
"password:" { send "xxo1#qaz\r" }
}
expect {
"*password*:" { send "xx1#qaz\r" }
}
expect "*]#"
send "df -Th\r"
expect "*]#"
send "exit\r"
expect eof
EOF
done
7、 密码过期需要批量修改密码
#!/bin/bash
for i in `cat root/soft/ip.txt`
do
usr/bin/expect <
spawn usr/bin/ssh root@$i
expect {
"UNIX password" { send "Huawei@123\r" }
}
expect {
"New password:" { send "xxHuzzawexxi@1234#\r" }
}
expect {
"Retype new password:" { send "xxHuzzawexxi@1234#\r" }
}
expect "*]#"
send "echo Huawei@123|passwd --stdin root\r"
expect "*]#"
send "exit\r"
expect eof
EOF
Done
四、expect 注意事项
1、expect 和 send 交互式执行
在服务器上测试过的语句,最后得出结论是spawn开启的新进程,后面的expect和send都是在和这个进程打交道,如果再重新开启一个spawn会自动退出上一个进程,然后重新开启。【比如我在图中spawn连接了远程服务器,执行一些语句,然后再开启一个spawn,那么第二个spawn会在本地运行,因为他们不属于同一个进程了。还有一点是,开启了spawn的进程中,输入send 发送交互语句的时候 在后面必须有一个expect,spawn 命令是出现交互式的前提下的 但是expect 匹配不上的话,那么它就会按照timeout 的设置进行等待
#!/usr/bin/expect
2
3 set timeout 30
4 spawn ssh 10.192.224.224
5 expect "password:"
6 send "mypassword\n"
7 expect "*$"
上面这个例子中send "mkdir tmpdir\n" #远程执行命令用send发送,不用spawn, expect "*$" #注意这个地方,要与操作系统上环境变量相匹配,尤其是有空格的情况下,一定在expct "*$ "把空格加上,加不上你就完蛋了。我试过。这个例子实际上是通过ssh 去登录远程机器,并且在远程机器上创建一个目录, 我们看到在我们输入密码后并没有去expect eof,这是因为ssh 这个spawn 并没 有结束,而且手动操作时ssh 实际上也不会自己结束除非你exit;所以你只能 expect bash 的提示符,当然也可以是机器名等,这样才可以在远程创建一个目 录。注意,请不要用spawn mkdir tmpdir,这样会使得上一个spawn 即ssh 结束,那 么你的tmpdir 将在本机建立。当然实际情况下可能会要你确认ssh key,可以通过并行的expect 进行处理,不 多赘述。
2、添加变量,send 中的语句执行必须要expect
#!/usr/bin/expect
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%"
2.1、注意代码刚开始的第一行,指定了expect的路径,与shell脚本相同,这一句指定了程序在执行时到哪里去寻找相应的启动程序。
2.2、代码刚开始还设定了timeout的时间为10秒,如果在执行scp任务时遇到了代码中没有指定的异常,则在等待10秒后该脚本的执行会自动终止。
2.3、spawn代表在本地终端执行的语句,在该语句开始执行后,expect开始捕获终端的输出信息,然后做出对应的操作。
2.4、expect代码中的捕获的(yes/no)内容用于完成第一次访问目标主机时保存密钥的操作。有了这一句,scp的任务减少了中断的情况。代码结尾的expect eof与spawn对应,表示捕获终端输出信息的终止。有了这段expect的代码,还只能完成对单个远程主机的scp任务。
2.5、如果需要实现批量scp的任务,则需要再写一个shell脚本来调用这个expect脚本。
3、expect 编写过程中空格请用“tab”键代替
4、当你执行expect脚本send一条命令的时候,会触发一次匹配动作—————》这个特性导致当你的命令中存在要匹配的关键字时,命令不会执行,而是永远能够匹配,进而影响预期执行效果!该特性通过执行expect -d打印debug信息能够观察到(可以通过清除缓存避免该问题)
5、多条send命令会并行执行,如果要串行,最好一条send一条expect
6、timeout和eof应该对应的是某个expect,也就是说,每个expect模块都应该设置timeout或eof响应行为
7、eof:当spawn新起的线程终止时(可以模拟kill即可),接受到eof信号,触发当前expect中的eof,把eof定义到全局是错误的行为,会报can not find channal named.
8、scp等执行完就结束的进程,可以直接通过捕获eof来判断是否执行完成
在多条件匹配时(一个expect里面多个表达式),expect会丢弃已经匹配的文本(或者说会在已经匹配到的文本末尾打个标记),当exp_continue再次进入时,会从标记位置往后继续匹配。也就是说,匹配的不是$expect_out(buffer)
9、注意,如果远程主机密码中有“$”、“#”这类特殊字符的话,在编写列表文件时就需要在这些特殊字符前加上转义字符,否则expect在执行时会输入错误的密码。
日常疑问:
1、免费软件和开源软件区别
开源软件:指公开源代码的软件。开源软件在发行的时候会附上软件的源代码,并授权允许用户更改、传播或者二次开发。
免费软件:免费提供给用户使用的软件,在免费的同时,通常也会有一些限制,比如源代码不公开,用户不能随意修改、不能二次发布等。