20.31 expect脚本同步文件
- 在一台机器上把文件同步到多台机器上
-
自动同步文件
#!/usr/bin/expect set passwd "123456" spawn rsync -av root@192.168.133.132:/tmp/12.txt /tmp/ expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } expect eof
[root@aiker script]# vim expect4.expect #!/usr/bin/expect set passwd "123456" spawn rsync -avzP -e "ssh -p 12220" root@localhost:/tmp/expect2.txt /tmp/ set timeout 200 expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } set timeout 200 ##使用timeout可以有效的解决进程超时退出 expect eof
加上执行权限,再来执行脚本
[root@aiker script]# ./expect4.expect
spawn rsync -avzP -e ssh -p 12220 root@localhost:/tmp/expect2.txt /tmp/
receiving incremental file list
expect2.txt
8 100% 7.81kB/s 0:00:00 (xfer#1, to-check=0/1)sent 30 bytes received 89 bytes 238.00 bytes/sec
total size is 8 speedup is 0.07
expect: spawn id exp6 not open
while executing
"expect eof"
(file "./expect4.expect" line 10)[root@aiker script]# ll /tmp/expect2.txt -rw-r--r-- 1 root root 8 May 4 01:28 /tmp/expect2.txt [root@aiker script]# cat !$ cat /tmp/expect2.txt 1232123 [root@aiker script]#
注释以下后,没有报错
#expect eof
#interact[root@aiker script]# vim expect4.expect #!/usr/bin/expect set passwd "1231312!" spawn rsync -avzP -e "ssh -p 12220" root@localhost:/tmp/expect2.txt /tmp/ set timeout 200 expect { "yes/no" { send "yes\r"} "password:" { send "$passwd\r" } } set timeout 200 #expect eof #interact
"expect4.expect" 11L, 237C written
[root@aiker script]# ./expect4.expect
spawn rsync -avzP -e ssh -p 12220 root@localhost:/tmp/expect2.txt /tmp/
receiving incremental file list
sent 11 bytes received 41 bytes 104.00 bytes/sec
total size is 8 speedup is 0.15
[root@aiker script]# !rm
rm -f /tmp/expect2.txt
[root@aiker script]# ./expect4.expect
spawn rsync -avzP -e ssh -p 12220 root@localhost:/tmp/expect2.txt /tmp/
receiving incremental file list
expect2.txt
8 100% 7.81kB/s 0:00:00 (xfer#1, to-check=0/1)
sent 30 bytes received 89 bytes 238.00 bytes/sec
total size is 8 speedup is 0.07
- set timeout 定义超时时间(单位为 秒) -1 为永远不超时
- 可以指定具体的秒数,比如5秒
# 20.32 expect脚本指定host和要同步的文件
[root@aiker script]# vim expect5.expect
#!/usr/bin/expect
set passwd "123456!"
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -av -e "ssh -p 12220" $file root@$host:$file
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect eof
[root@aiker script]# chmod +x !$
chmod +x expect5.expect
[root@aiker script]# ./expect5.expect localhost "/tmp/test5.txt"
spawn rsync -av -e ssh -p 12220 /tmp/test5.txt root@localhost:/tmp/test5.txt
sending incremental file list
test5.txt
sent 83 bytes received 31 bytes 228.00 bytes/sec
total size is 6 speedup is 0.05
expect: spawn id exp6 not open
while executing
"expect eof"
(file "./expect5.expect" line 10)
[root@aiker script]# ssh -p 12220 root@localhost
Last login: Fri May 4 01:46:56 2018 from localhost
[root@test220 ~]# ll /tmp/test5.txt
-rw-r--r-- 1 root root 6 May 4 2018 /tmp/test5.txt
[root@test220 ~]# cat !$
cat /tmp/test5.txt
12345
如果脚本中设置set timeout -1永远不超时
[root@aiker script]# !vim
vim expect5.expect
#!/usr/bin/expect
set passwd "ederew1!"
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -av -e "ssh -p 12220" $file root@$host:$file
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
#expect eof
set timeout 180
[root@aiker script]# ./expect5.expect localhost "/tmp/test5.txt"
spawn rsync -av -e ssh -p 12220 /tmp/test5.txt root@localhost:/tmp/test5.txt
sending incremental file list
sent 34 bytes received 12 bytes 92.00 bytes/sec
total size is 6 speedup is 0.13
# 20.33 构建文件分发系统
- 需求背景
对于大公司而言,肯定时不时会有网站或者配置文件更新,而且使用的机器肯定也是好多台,少则几台,多则几十甚至上百台。所以,自动同步文件是至关重要的。
- 实现思路
首先要有一台模板机器,把要分发的文件准备好,然后只要使用expect脚本批量把需要同步的文件分发到目标机器即可。
- 核心命令
rsync -av --files-from=list.txt / root@host:/
- 使用rsync 的 –files参数,可以实现调用文件里面的列表,进行多个文件远程传输,进而实现文件分发 文件列表的路径必须要以全局路径,绝对路径
- 文件分发系统的实现
[root@lnmp script]# cat rsync.expect #expect自动脚本
#!/usr/bin/expect
#set passwd "123456"
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
- rsync -avR --files-from=$file / root@$host:/ 内容里加了一个-R ,因为不能保证对方机器上有相同的路径,加上-R就会 自动创建没有的路径
- 1.同步的路径,需要保证对方机器也有这个相同的路径,如果没有路径,需要使用 -R 创建路径
- 需要一个ip.list,因为你的远程同步的机器不止一台,所以还要加上一个ip列表
[root@lnmp script]# cat ip.list #ip host
172.16.22.220
172.16.22.221
172.16.22.222
172.16.22.223
- 因为实现分发系统,肯定是因为需要分发的机器数量过大,所以需要定义好 文件 的 list 列表文件
[root@lnmp script]# cat list.txt #文件列表
/root/script/num.txt
/root/script/hitrate.sh
/root/script/logrotate.sh
/root/script/accessnum.sh
- 做这个expect脚本保证俩台机器的密码是一样的,如果不一样,就只能挨个去定义每台机器的密码
- 下面还需要创建一个rsync.sh
- 这个sh 的目的,就是遍历一下 ip列表文件中的 ip地址
[root@lnmp script]# cat rsync.sh #循环同步脚本
#!/bin/bash
for ip in $(cat ip.list)
do
echo $ip
./rsync.expect $ip list.txt
done
chmod +x rsync.*
执行结果
[root@lnmp script]# sh -x rsync.sh
++ cat ip.list
-
for ip in '$(cat ip.list)'
- echo 172.16.22.220
172.16.22.220
- ./rsync.expect 172.16.22.220 list.txt
spawn rsync -avR --files-from=list.txt / root@172.16.22.220:/
The authenticity of host '172.16.22.220 (172.16.22.220)' can't be established.
ECDSA key fingerprint is b0:78:a9:37:02:52:79:a8:ff:bc:76:de:be:4e:01:94.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.22.220' (ECDSA) to the list of known hosts.
building file list ... done
root/
root/script/
root/script/accessnum.sh
root/script/hitrate.sh
root/script/logrotate.sh
root/script/num.txt
sent 1872 bytes received 94 bytes 3932.00 bytes/sec
total size is 1556 speedup is 0.79
-
for ip in '$(cat ip.list)'
- echo 172.16.22.221
172.16.22.221
- ./rsync.expect 172.16.22.221 list.txt
spawn rsync -avR --files-from=list.txt / root@172.16.22.221:/
building file list ... done
root/
root/script/
root/script/accessnum.sh
root/script/hitrate.sh
root/script/logrotate.sh
root/script/num.txt
sent 1872 bytes received 94 bytes 1310.67 bytes/sec
total size is 1556 speedup is 0.79
expect: spawn id exp6 not open
while executing
"expect eof"
(file "./rsync.expect" line 10)
-
for ip in '$(cat ip.list)'
- echo 172.16.22.222
172.16.22.222
- ./rsync.expect 172.16.22.222 list.txt
spawn rsync -avR --files-from=list.txt / root@172.16.22.222:/
building file list ... done
root/
root/script/
root/script/accessnum.sh
root/script/hitrate.sh
root/script/logrotate.sh
root/script/num.txt
sent 1872 bytes received 94 bytes 3932.00 bytes/sec
total size is 1556 speedup is 0.79
expect: spawn id exp6 not open
while executing
"expect eof"
(file "./rsync.expect" line 10)
-
for ip in '$(cat ip.list)'
- echo 172.16.22.223
172.16.22.223
- ./rsync.expect 172.16.22.223 list.txt
spawn rsync -avR --files-from=list.txt / root@172.16.22.223:/
The authenticity of host '172.16.22.223 (172.16.22.223)' can't be established.
ECDSA key fingerprint is b0:78:a9:37:02:52:79:a8:ff:bc:76:de:be:4e:01:94.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.22.223' (ECDSA) to the list of known hosts.
building file list ... done
root/
root/script/
root/script/accessnum.sh
root/script/hitrate.sh
root/script/logrotate.sh
root/script/num.txt
sent 1872 bytes received 94 bytes 3932.00 bytes/sec
total size is 1556 speedup is 0.79
**注意:**
- expect脚本 必须加入执行权限
- 文件不存在,会报错
- 分发系统还有一个重要的关键是,确保同步的机器的密码一致,否则将不能实现同步;所以这就存在一个弊端,一旦脚本暴露,将会让别人知道如何登陆你机器;当然也有对应的解决办法,那就是使用密钥认证,这样的话,自然在命令行业省去“输入密码< password:" { send "$passwdr" } >''”和“定义密码< set passwd "123456" >”的命令了
# 20.34 批量远程执行命令
构建批量执行命令的脚本
[root@lnmp exe]# cat exe.expect
#!/usr/bin/expect
set host [lindex $argv 0]
#set passwd "123456"
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"
#cat exe.sh
#!/bin/bash
for ip in $(cat ip.list)
do
echo $ip
./exe.expect $ip "hostname;w"
done
#cat ip.list
172.16.22.220
172.16.22.221
172.16.22.222
172.16.22.223
添加执行权限:
[root@lnmp exe]# chmod +x exe*
[root@lnmp exe]# ll
total 12
-rwxr-xr-x 1 root root 232 May 4 21:58 exe.expect
-rwxr-xr-x 1 root root 88 May 4 22:00 exe.sh
-rw-r--r-- 1 root root 56 May 4 22:00 ip.list
[root@lnmp exe]# sh -x exe.sh
++ cat ip.list
-
for ip in '$(cat ip.list)'
- echo 172.16.22.220
172.16.22.220
- ./exe.expect 172.16.22.220 'hostname;w'
spawn ssh root@172.16.22.220
Last login: Fri May 4 11:41:49 2018 from 172.16.22.224
[root@test220 ~]# hostname;w
test220
22:01:46 up 49 days, 1:57, 1 user, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.22.246 22:01 2.00s 0.02s 0.00s w
[root@test220 ~]# + for ip in '$(cat ip.list)'
- echo 172.16.22.221
172.16.22.221
- ./exe.expect 172.16.22.221 'hostname;w'
spawn ssh root@172.16.22.221
Last login: Thu May 3 03:03:41 2018 from 172.16.22.220
[root@test221 ~]# hostname;w
test221
22:09:14 up 49 days, 1:48, 1 user, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.22.246 22:09 2.00s 0.04s 0.00s w
[root@test221 ~]# + for ip in '$(cat ip.list)'
- echo 172.16.22.222
172.16.22.222
- ./exe.expect 172.16.22.222 'hostname;w'
spawn ssh root@172.16.22.222
Last login: Fri May 4 21:55:57 2018 from 172.16.58.8
[root@test222 ~]# hostname;w
test222
22:09:24 up 48 days, 1:14, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/1 172.16.58.8 21:55 13:24 0.01s 0.01s -bash
root pts/2 172.16.22.246 22:09 4.00s 0.02s 0.00s w
[root@test222 ~]# + for ip in '$(cat ip.list)'
- echo 172.16.22.223
172.16.22.223
- ./exe.expect 172.16.22.223 'hostname;w'
spawn ssh root@172.16.22.223
Last login: Fri May 4 21:16:20 2018 from localhost
[root@test223 ~]# hostname;w
test223
22:01:53 up 36 days, 13:12, 1 user, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 172.16.22.246 22:01 1.00s 0.01s 0.00s w
# 扩展:
## shell多线程
在shell脚本中,我们想要实现多进程高并发,最简单的方法是把命令丢到后台去,如果量不大的话,没问题。 但是如果有几百个进程同一时间丢到后台去就很恐怖了,对于服务器资源的消耗非常大,甚至导致宕机。
那有没有好的解决方案呢? 当然有!
我们先来学习下面的常识。
**1 文件描述符**
文件描述符(缩写fd)在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。每一个unix进程,都会拥有三个标准的文件描述符,来对应三种不同的流:
![](http://blog.lishiming.net/wp-content/uploads/2017/11/fifo2.png)
除了上面三个标准的描述符外,我们还可以在进程中去自定义其他的数字作为文件描述符,后面例子中会出现自定义数字。每一个文件描述符会对应一个打开文件,同时,不同的文件描述符也可以对应同一个打开文件;同一个文件可以被不同的进程打开,也可以被同一个进程多次打开。
我们可以写一个测试脚本/tmp/test.sh,内容如下:
#!/bin/bash
echo “该进程的pid为$$”
exec 1>/tmp/test.log 2>&1
ls -l /proc/$$/fd/
执行该脚本 sh /tmp/test.sh,然后查看/tmp/test.log内容为:
总用量 0
lrwx—— 1 root root 64 11月 22 10:26 0 -> /dev/pts/3
l-wx—— 1 root root 64 11月 22 10:26 1 -> /tmp/test.log
l-wx—— 1 root root 64 11月 22 10:26 2 -> /tmp/test.log
lr-x—— 1 root root 64 11月 22 10:26 255 -> /tmp/test.sh
lrwx—— 1 root root 64 11月 22 10:26 3 -> socket:[196912101]
其中0为标准输入,也就是当前终端pts/3,1和2全部指向到了/tmp/test.log,另外两个数字,咱们暂时不关注。
**2 命名管道**
我们之前接触过的管道“1”,其实叫做匿名管道,它左边的输出作为右边命令的输入。这个匿名管道只能为两边的命令提供服务,它是无法让其他进程连接的。
![](http://blog.lishiming.net/wp-content/uploads/2017/11/fifo.png)
实际上,这两个进程(cat和less)并不知道管道的存在,它们只是从标准文件描述符中读取数据和写入数据。
另外一种管道叫做命名管道,英文(First In First Out,简称FIFO)。
FIFO本质上和匿名管道的功能一样,只不过它有一些特点:
1)在文件系统中,FIFO拥有名称,并且是以设备特俗文件的形式存在的;
2)任何进程都可以通过FIFO共享数据;
3)除非FIFO两端同时有读与写的进程,否则FIFO的数据流通将会阻塞;
4)匿名管道是由shell自动创建的,存在于内核中;而FIFO则是由程序创建的(比如mkfifo命令),存在于文件系统中;
5)匿名管道是单向的字节流,而FIFO则是双向的字节流;
有了上面的基础知识储备后,下面我们来用FIFO来实现shell的多进程并发控制。
需求背景:
领导要求小明备份数据库服务器里面的100个库(数据量在几十到几百G),需要以最快的时间完成(5小时内),并且不能影响服务器性能。
需求分析:
由于数据量比较大,单个库备份时间少则10几分钟,多则几个小时,我们算平均每个库30分钟,若一个库一个库的去备份,则需要3000分钟,相当于50个小时。很明显不可取。但全部丢到后台去备份,100个并发,数据库服务器也无法承受。所以,需要写一个脚本,能够控制并发数就可以实现了。
控制并发的shell脚本示例:
#!/bin/sh
function a_sub {
sleep 2;
endtime=date +%s
sumtime=$[$endtime-$starttime]
echo “我是$i,运行了2秒,整个脚本已经执行了$sumtime秒”
}
starttime=date +%s
export starttime
##其中$$为该进程的pid
tmp_fifofile=”/tmp/$$.fifo”
##创建命名管道
mkfifo $tmp_fifofile
##把文件描述符6和FIFO进行绑定
exec 6<>$tmp_fifofile
##绑定后,该文件就可以删除了
rm -f $tmp_fifofile
##并发量为3,用这个数字来控制并发数
thread=3
for ((i=0;i<$thread;i++));
do
##写一个空行到管道里,因为管道文件的读取以行为单位
echo >&6
done
##循环10次,相当于要备份100个库
for ((i=0;i<10;i++))
do
##读取管道中的一行,每次读取后,管道都会少一行
read -u6
{
a_sub || {echo “a_sub is failed”}
##每次执行完a_sub函数后,再增加一个空行,这样下面的进程才可以继续执行
echo >&6
} & ##这里要放入后台去,否则并发实现不了
done
##这里的wait意思是,需要等待以上所有操作(包括后台的进程)都结束后,再往下执行。
wait
##关闭文件描述符6的写
exec 6>&-
转载于:https://blog.51cto.com/235571/2141231