十七周四次课

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值