20.27 分发系统介绍
介绍 expect 使用场景
expect可以让我们实现自动登录远程机器,并且可以实现自动远程执行命令。当然若是使用不带密码的密钥验证同样可以实现自动登录和自动远程执行命令。但当不能使用密钥验证的时候,我们就没有办法了。所以,这时候只要知道对方机器的账号和密码就可以通过expect脚本实现登录和远程命令。
使用之前先安装 expect 软件
[root@liuhongwei-01 ~]# yum install -y expect
20.28 expect脚本远程登录
自动远程登录,登陆后不退出
首先编写脚本
[root@liuhongwei-01 shell]# vim ex1.sh
#!/usr/bin/expect
set host "192.168.93.128"
set passwd "xxxxxx"
spawn ssh root@$host
expect {
"yes/no" { send "yes\r"; exp_continue}
"assword:" { send "$passwd\r" }
}
interact
然后给脚本加上执行权限 a+x
[root@liuhongwei-01 shell]# ./ex1.sh
-bash: ./ex1.sh: 权限不够
[root@liuhongwei-01 shell]# chmod a+x ex1.sh
[root@liuhongwei-01 shell]# ./ex1.sh
spawn ssh root@192.168.93.128
The authenticity of host '192.168.93.128 (192.168.93.128)' can't be established.
ECDSA key fingerprint is 51:1c:64:b5:38:a5:ef:75:d9:a3:17:c6:fc:b2:b1:dd.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.93.128' (ECDSA) to the list of known hosts.
root@192.168.93.128's password:
Last login: Thu Aug 2 09:34:39 2018 from 192.168.93.1
[root@hongwei-02 ~]#
20.29 expect脚本远程执行命令
首先还是编写脚本
[root@liuhongwei-01 shell]# vim ex2.sh
#!/usr/bin/expect
set user "root"
set passwd "xxxx"
spawn ssh $user@192.168.93.128
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "$passwd\r" }
}
expect "]*" 这一个命令是遇到]* *表示通配,遇到这样的符号我们就执行下面的操作
send "touch /tmp/12.txt\r" \r表示回车
expect "]*"
send "echo 1212 > /tmp/12.txt\r"
expect "]*"
send "exit\r"
~
给脚本加上执行权限
[root@liuhongwei-01 shell]# chmod a+x ex2.sh
执行这个脚本
[root@liuhongwei-01 shell]# ./ex2.sh
spawn ssh root@192.168.93.128
root@192.168.93.128's password:
Last login: Thu Aug 2 09:49:23 2018 from 192.168.93.129
[root@hongwei-02 ~]# touch /tmp/12.txt
[root@hongwei-02 ~]# echo 1212 > /tmp/12.txt
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]#
我们进入到128的机器,然后执行二条命令并退出,回到128机器上看看有没有刚才创建的文件
[root@hongwei-02 ~]# ls /tmp/
aming.sock db1.sql hsperfdata_root keepalived.conf mysql.sock php-fcgi.sock test.com.log test.sql zrlog.sql
[root@hongwei-02 ~]# date
2018年 08月 02日 星期四 09:45:23 CST
[root@hongwei-02 ~]# ls -l /tmp/
总用量 4
-rw-r--r-- 1 root root 5 8月 2 09:50 12.txt
srw-rw-rw- 1 root root 0 8月 2 09:31 aming.sock
srwxrwxrwx 1 mysql mysql 0 8月 2 09:31 mysql.sock
srw-rw-rw- 1 root root 0 8月 2 09:31 php-fcgi.sock
[root@hongwei-02 ~]# cat /tmp/12.txt
1212
20.30 expect脚本传递参数
expect也是可以传递参数,想shell的$1 $2,相当于内置变量
还是编写脚本
[root@liuhongwei-01 shell]# vim ex3.sh
#!/usr/bin/expect
set user [lindex $argv 0] 第一个参数
set host [lindex $argv 1] 第二个参数
set passwd "xxxxx"
set cm [lindex $argv 2] 第三个参数,也就是我们要执行的命令
spawn ssh $user@$host
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect "]*"
send "$cm\r"
expect "]*"
send "exit\r"
[root@liuhongwei-01 shell]# chmod a+x ex3.sh
然后执行脚本
[root@liuhongwei-01 shell]# ./ex3.sh root 192.168.93.128 ls
spawn ssh root@192.168.93.128
root@192.168.93.128's password:
Last login: Thu Aug 2 09:50:19 2018 from 192.168.93.129
[root@hongwei-02 ~]# ls
1.txt aming.txt anaconda-ks.cfg log logs php-7.1.6.tar.bz2 temp zabbix-release-3.2-1.el7.noarch.rpm
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]#
执行的时候要加上 用户root ip 和要执行的命令,也就是上面所说的第一个参数 root 第二个参数 ip 第三个参数要执行的命令ls、
如果想要执行多条命令如下:
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]# ./ex3.sh root 192.168.93.128 "ls;w;netstat -lntp"
spawn ssh root@192.168.93.128
root@192.168.93.128's password:
Last login: Thu Aug 2 09:58:34 2018 from 192.168.93.129
[root@hongwei-02 ~]# ls;w;netstat -lntp
1.txt aming.txt anaconda-ks.cfg log logs php-7.1.6.tar.bz2 temp zabbix-release-3.2-1.el7.noarch.rpm
10:00:31 up 30 min, 2 users, load average: 0.00, 0.01, 0.08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.93.1 09:34 9:03 0.02s 0.02s -bash
root pts/1 192.168.93.129 10:00 0.00s 0.06s 0.04s w
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 752/rpcbind
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2051/nginx: master
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 985/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2099/master
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 2051/nginx: master
tcp6 0 0 :::3306 :::* LISTEN 2266/mysqld
tcp6 0 0 :::111 :::* LISTEN 752/rpcbind
tcp6 0 0 :::22 :::* LISTEN 985/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2099/master
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]#
20.31 expect脚本同步文件
编写脚本
[root@liuhongwei-01 shell]# vim ex4.sh
#!/usr/bin/expect
set passwd "xxxxx"
spawn rsync -av root@192.168.93.128:/tmp/12.txt /tmp/ 我们把远程机器的tmp目录的12.txt同步到本机的tmp目录下
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof
[root@liuhongwei-01 shell]# chmod a+x ex4.sh
执行脚本
[root@liuhongwei-01 shell]# ./ex4.sh
spawn rsync -av root@192.168.93.128:/tmp/12.txt /tmp/
root@192.168.93.128's password:
receiving incremental file list
12.txt
sent 43 bytes received 96 bytes 92.67 bytes/sec
total size is 5 speedup is 0.04
[root@liuhongwei-01 shell]#
看一下本机的tmp目录下的12.txt
[root@liuhongwei-01 shell]# cat /tmp/12.txt
1212
[root@liuhongwei-01 shell]# ls -l /tmp/12.txt
-rw-r--r-- 1 root root 5 8月 2 09:50 /tmp/12.txt
我们把脚本中最后的expect eof注释掉的话,就是还没有同步就直接退出来了
spawn rsync -av root@192.168.93.128:/tmp/12.txt /tmp/
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
#expect eof
[root@liuhongwei-01 shell]# ./ex4.sh
spawn rsync -av root@192.168.93.128:/tmp/12.txt /tmp/
root@192.168.93.128's password: [root@liuhongwei-01 shell]#
20.32 expect脚本指定host和要同步的文件
编写脚本
[root@liuhongwei-01 shell]# vim ex5.sh
#!/usr/bin/expect
set passwd "w撒大声地6"
set host [lindex $argv 0] 第一个参数是host
set file [lindex $argv 1] 第二个参数是file
spawn rsync -av $file root@$host:$file 这个file是主机到对方机器,路径要写绝对路径
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof
[root@liuhongwei-01 shell]# chmod a+x ex5.sh
执行脚本前我们创建一个1234文件,然后看一下执行结果
[root@liuhongwei-01 shell]# ls /tmp/
12.txt hsperfdata_root php-fcgi.sock zhangsan
aminglinux keepalived+nginx.conf systemd-private-b066e0862e624a66895ce4cf3f270aa0-httpd.service-xqI3bJ
aming.sock mysql.sock yangying
[root@liuhongwei-01 shell]# touch /tmp/1234.txt
[root@liuhongwei-01 shell]# ls /tmp/
1234.txt aming.sock mysql.sock yangying
12.txt hsperfdata_root php-fcgi.sock zhangsan
aminglinux keepalived+nginx.conf systemd-private-b066e0862e624a66895ce4cf3f270aa0-httpd.service-xqI3bJ
[root@liuhongwei-01 shell]# ./ex5.sh 192.168.93.128 "/tmp/1234.txt"
spawn rsync -av /tmp/1234.txt root@192.168.93.128:/tmp/1234.txt
root@192.168.93.128's password:
sending incremental file list
1234.txt
sent 90 bytes received 35 bytes 83.33 bytes/sec
total size is 0 speedup is 0.00
看一下远程机器有没有
[root@liuhongwei-01 shell]# ./ex1.sh
spawn ssh root@192.168.93.128
root@192.168.93.128's password:
Last failed login: Thu Aug 2 10:07:47 CST 2018 from 192.168.93.129 on ssh:notty
There were 2 failed login attempts since the last successful login.
Last login: Thu Aug 2 10:00:31 2018 from 192.168.93.129
[root@hongwei-02 ~]# ls /tmp
1234.txt 12.txt aming.sock mysql.sock php-fcgi.sock
[root@hongwei-02 ~]#
20.33 构建文件分发系统
首先定义rsync.expect脚本内容:
[root@hongwei-02 ~]# vim rsync.expect
#!/usr/bin/expect
set passwd "xxxxx"
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -av --files-from=$file / root@$host:/ 这个file定义的就是list.txt而不是一个文件
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof
然后再定义一个list.txt
vim /tmp/list.txt
/tmp/12.txt
/root/shell/1.sh
/tmp/1234.txt
这个文件就是同步的文件,必须保证文件路径对方机器也有,文件不需要保证,但是我们可以在rsync.expect文件中加上R选项会自动帮你创建。
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -avR --files-from=$file / root@$host:/
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof
然后还需要加上一个ip.txt
[root@hongwei-02 ~]# vim /tmp/ip.txt
192.168.93.128
127.0.0.1
做这个实验的时候,需要两台机器的密码都是一样的。但是可以使用秘钥认证
然后在编写一个rsync的shell脚本
[root@liuhongwei-01 shell]# vim rsync.sh
#!/bin/bash
for ip in `cat /tmp/ip.list`
do
echo $ip
./rsync.expect $ip /tmp/list.txt
done
然后要给rsync.expect执行权限
[root@liuhongwei-01 shell]# chmod a+x rsync.expect
然后执行rsync.sh脚本
[root@liuhongwei-01 shell]# sh -x rsync.sh
++ cat /tmp/ip.txt
+ for ip in '`cat /tmp/ip.txt`'
+ echo 192.168.93.128
192.168.93.128
+ ./rsync.expect 192.168.93.128 /tmp/list.txt
spawn rsync -avR --files-from=/tmp/list.txt / root@192.168.93.128:/
root@192.168.93.128's password:
building file list ... done
root/
root/shell/
root/shell/1.sh
tmp/
sent 274 bytes received 44 bytes 212.00 bytes/sec
total size is 64 speedup is 0.20
+ for ip in '`cat /tmp/ip.txt`'
+ echo 127.0.0.1
127.0.0.1
+ ./rsync.expect 127.0.0.1 /tmp/list.txt
spawn rsync -avR --files-from=/tmp/list.txt / root@127.0.0.1:/
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ECDSA key fingerprint is 51:1c:64:b5:38:a5:ef:75:d9:a3:17:c6:fc:b2:b1:dd.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts.
root@127.0.0.1's password: [root@liuhongwei-01 shell]#
然后我回到2机器上看看有没有同步成功
[root@hongwei-02 ~]# ls -l /root/shell/1.sh
-rwxr-xr-x 1 root root 59 7月 27 21:22 /root/shell/1.sh
[root@liuhongwei-01 shell]# ls -l /root/shell/1.sh
-rwxr-xr-x 1 root root 59 7月 27 21:22 /root/shell/1.sh
20.34 批量远程执行命令
首先编写脚本
[root@liuhongwei-01 shell]# vim exe.expect
#!/usr/bin/expect
set host [lindex $argv 0]
set passwd "sdsd"
set cm [lindex $argv 1]
spawn ssh root@$host
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect "]*"
send "$cm\r"
expect "]*"
send "exit\r"
[root@liuhongwei-01 shell]# chmod a+x !$
chmod a+x exe.expect
[root@liuhongwei-01 shell]#
然后在编写exe的shell脚本,就是for循环
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]# vim exe.sh
#!/bin/bash
for ip in `cat /tmp/ip.txt`
do
./exe.expect $ip "w;ls /tmp"
done
然后执行
[root@liuhongwei-01 shell]# sh exe.sh
spawn ssh root@192.168.93.128
root@192.168.93.128's password:
Last login: Thu Aug 2 11:00:19 2018 from 192.168.93.129
[root@hongwei-02 ~]# w;ls /tmp
11:00:46 up 1:30, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.93.1 09:34 10:54 0.10s 0.10s -bash
root pts/1 192.168.93.129 11:00 0.00s 0.01s 0.01s w
1234.txt 12.txt aming.sock ip.txt list.txt mysql.sock php-fcgi.sock
[root@hongwei-02 ~]# [root@liuhongwei-01 shell]#
expect的核心是spawn expect send set
spawn:调用要执行的命令
expect:等待命令提示信息的出现,也就是捕捉用户输入的提示:
send:发送需要交互的值,替代了用户手动输入内容
set:设置变量值
interact:执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。
expect eof:这个一定要加,与 spawn 对应表示捕获终端输出信息终止,类似于 if....endif
expect 脚本必须以 interact 或 expect eof 结束,执行自动化任务通常 expect eof 就够了。
2. 设置 timeout
设置 expect 永不超时
set timeout -1
设置 expect 300 秒超时,如果超过 300 没有 expect 内容出现,则推出
set timeout 300