默认的情况下,Shell脚本中的命令是串行执行的,必须等到前一条命令执行完后才执行接下来的命令,但是如果我有一大批的的命令需要执行,而且互相又没有影响的情况下(有影响的话就比较复杂了),那么就要使用命令的并发执行了。
如下:
#!/bin/bash IPLIST=/home/meta/ipinfo/iplist for i in $(cat ${IPLIST} |grep -viE "^#|备机|ts"|awk ‘{print $1}‘) do ssh $i "cd ~/update/;tar zxf patch-20160909.tgz -C ~/LMDG/ && echo ‘/$i ok‘ || echo ‘/$i bad‘" done >> result.txt echo "resutl"|mutt -a result.txt -s update-result [email protected]126.com
对于上面的代码,因为 iplist 中有好多ip,每个”tar zxf”都挺耗时的,所以打算使用并发编程,这样就可以节省大量时间了。
修改如下:
#!/bin/bash IPLIST=/home/meta/ipinfo/iplist for i in $(cat ${IPLIST} |grep -viE "^#|备机|ts"|awk ‘{print $1}‘) do ssh $i "cd ~/update/;tar zxf patch-20160909.tgz -C ~/LMDG/ && echo ‘/$i ok‘ || echo ‘/$i bad‘" & done >> result.txt echo "resutl"|mutt -a result.txt -s update-result [email protected]126.com
加上“&” 之后 “tar zxf”就可以并行执行了。 实质是将”tar zxf” 作为后台进程在执行,这样该命令就不会占用当前bash,其他命令也不用等待前面命令执行完再继续了,而且可以放入多个任务到后台,这样就实现了多任务并发。
我本来目的是让”tar zxf”这个循环都执行结束后,再“mutt”前面的结果。如果像上面这样写的话,在”tar zxf”都还没结束时就已经开始执行“mutt”了,得到了错误的结果,因此需要做如下修改:
#!/bin/bash IPLIST=/home/meta/ipinfo/iplist for i in $(cat ${IPLIST} |grep -viE "^#|备机|ts"|awk ‘{print $1}‘) do ssh $i "cd ~/update/;tar zxf patch-20160909.tgz -C ~/LMDG/ && echo ‘/$i ok‘ || echo ‘/$i bad‘" & done >> result.txt wait echo "resutl"|mutt -a result.txt -s update-result [email protected]126.com
这里添加了“wait” 之后就可以达到我们预期的效果了,wait的作用就是等待子任务都执行完之后在结束父任务,继而执行下面的任务。
但是,紧接着又有问题了,如果这个iplist中的量巨大,这样一口气都放到后台,系统超出负载后,会有性能变差或者宕机风险,因此我们需要一个控制并发数的机制。
因此我们引入了任务队列的概念,有点类似之前socket举例中的消费者生产者模型,通过消息队列来调节供需的不平衡
修改如下:
#!/bin/bash IPLIST=/home/meta/ipinfo/iplist #任务(消费者) THREAD=50 #声明并发线程并发个数,这个是此应用的关键,也就是设置管道的最大任务数 TMPFIFO=/tmp/$$.fifo #声明管道名称,‘$$‘表示脚本当前运行的进程PID mkfifo $TMPFIFO #创建管道 exec 5<>${TMPFIFO} #创建文件标示符“5”,这个数字可以为除“0”、“1”、“2”之外的所有未声明过的字符,以读写模式操作管道文件;系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变,换句话说就是在调用进程内部执行一个可执行文件 rm -rf ${TMPFIFO} #清除创建的管道文件 #为并发线程创建同样个数的占位 for((i=1;i<=$THREAD;i++)) do echo ; #借用read命令一次读取一行的特性,使用一个echo默认输出一个换行符,来确保每一行只有一个线程占位;这里让人联想到生产者&消费者模型,管道文件充当消息队列,来记录消费者的需求,然后由生产者去领任务,并完成任务,这里运用了异步解耦的思想。 done >&5 #将占位信息写入管道 for i in $(cat ${IPLIST} |grep -viE "^#|备机|ts"|awk ‘{print $1}‘) #从任务队列中依次读取任务 do read -u5 #从文件描述符管道中,获取一个管道的线程占位然后开始执行操作;read中 -u 后面跟fd,表示从文件描述符中读入,该文件描述符可以是exec新开启的。 { echo $(cat ~/ipinfo/iplist|grep $i|awk ‘{print $2}‘); ssh -oConnectTimeout=10 -oConnectionAttempts=3 $i "cd /home/Log/;grep ‘MIL‘ mission_2016-08-03*.log |awk -F, ‘{if(\$19==1370) print \$0}‘| awk -F, ‘{if(\$20==0) print \$0}‘>miss_info.txt" echo "" >&5 #任务执行完后在fd5中写入一个占位符,以保证这个线程执行完后,线程继续保持占位,继而维持管道中永远是50个线程数,&表示该部分命令/任务放入后台不占当前的bash,实现并行处理 } & done wait #等待父进程的子进程都执行结束后再结束父进程 exec 5>&- #关闭fd5的管道 exit 0
————————————————
版权声明:本文为CSDN博主「白金牧场」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34409701/article/details/52488964
原文地址:https://www.cnblogs.com/cangqinglang/p/12105399.html