shell脚本多任务并发执行介绍(ping批量检测主机是否在线脚本)

tech2023-10-10  104

文章目录

说明非并发原理并发原理及使用方法解决并发无序问题 以ping某段主机是否在线对并发进行说明单命令使用(无依赖关系)普通脚本多并发脚本 多命令使用(有依赖关系)普通脚本多并发脚本

说明

非并发原理

一般脚本正常情况下执行过程是按顺序执行的,简单来说就是上一个执行完毕了才会进行下一个;

代码和执行结果如下:

[root@controller ccx]# cat ping.sh for i in {1..10} ; do echo $i sleep 1 done echo "END" [root@controller ccx]# [root@controller ccx]# sh ping.sh #下面结果是1秒出一次 1 2 3 4 5 6 7 8 9 10 END [root@controller ccx]#

上面中可以看到,循环体中的“echo $i”命令是串行执行的。但是如果所执行的命令耗时比较长,这就会导致整个程序的执行时间非常长,甚至可能导致程序执行时卡在那里,长时间失去响应。

并发原理及使用方法

如果使用并发执行,就不需要等待上一条命令执行完毕再执行上一条,相当于多个任务同时进行,所以完成时间也就大大提升,具体的实现方法很简单,就是在要并发执行的命令后面加上&,将其转入后台执行,这样就可以在执行完一条命令之后,不必等待其执行结束,就立即转去执行下一条命令。

代码和执行结果如下:

[root@controller ccx]# cat for.sh for i in {1..10} ; do echo $i & #sleep 1 & done [root@controller ccx]# [root@controller ccx]# sh for.sh [root@controller ccx]# 3 4 8 9 5 7 10 2 6 1 [root@controller ccx]#

上述代码,为了更好的看出并发执行,我把sleep给注释了,可以看到,不仅没有顺序,而且有一个并不是运行结果(10个参数实际上只有9个值)

下面我们把sleep取消注释,而且让sleep也并发运行,下面运行结果也是一瞬间执行完毕,并且每一个参数都会执行完毕,也就是说sleep是有作用的,只是并发了一瞬间完成而已。 注:加个sleep虽然能解决参数有一个参数无法返回结果的问题,但执行次数多了会偶尔出现一次有一个参数无法返回结果,所以这不建议用,正规解决方法是 在脚本最后加上 wait,下面说明!

[root@controller ccx]# cat for.sh for i in {1..10} ; do echo $i & sleep 1 & done [root@controller ccx]# sh for.sh 1 2 7 4 6 3 5 8 9 10

解决并发无序问题

前面说过了并发就是在命令后面加上&即可,但在并发执行时不能保证命令的执行顺序,如下图,本应在整个循环执行结束之后再执行的echo "END"命令,却会在程序中间被执行。所以在并发执行时,我们通常都需要保证在循环体中的所有命令都执行完后再向后执行接下来的命令,这时就可以使用 wait命令来实现。在Shell中使用wait命令,相当于其它高级语言里的多线程同步。 下面对代码进行改进,增加wait命令:

[root@controller ccx]# cat for.sh for i in {1..10} ; do echo $i & #sleep 1 & done wait echo "END" [root@controller ccx]# sh for.sh 1 5 6 10 4 9 8 7 2 3 END [root@controller ccx]# sh for.sh 2 5 7 8 6 10 4 9 1 3 END

可以看到,加了wait命令执行结果就正常了,如论循环怎么无序,END都会等for循环中结束了才会打印。

下面我会以ping主机是否能通的一个列子对并发进行说明。

以ping某段主机是否在线对并发进行说明

编写一个脚本,扫描192.168.198.0/24网络里,当前在线的主机有哪些,能ping通就认为在线。

单命令使用(无依赖关系)

单命令就是只有一条命令输出结果,没有多条命令相互依赖的情况。 执行原理就是直接把该命令放后台,这个程序的所有过程都同步进行,无需等待上一个命令执行完毕!

普通脚本

编写一个脚本,扫描192.168.198.0/24网络里,当前在线的主机有哪些,能ping通就认为在线,下面脚本是一个最简单且只显示ping得通的ip,代码和执行结果如下:

[root@controller ccx]# cat ping2.sh for i in {128..133};do ip="192.168.198.$i" ping -c 2 $ip &> /dev/null && echo $ip is up done echo "END" [root@controller ccx]# sh ping2.sh 192.168.198.129 is up 192.168.198.130 is up END [root@controller ccx]#

注:如上代码执行过程中不能ctrl+c结束,必须用ctrl+z转入后台的形式结束。 如上代码,实现过程是一条一条依次出现的,所以上面6个IP花了大概30秒,如果60个600个IP花的时间可想而知。

多并发脚本

我们对上普通代码加上 多并发格式即可,&和wait,代码和执行结果如下:

[root@controller ccx]# cat ping2.sh for i in {128..133};do ip="192.168.198.$i" ping -c 2 $ip &> /dev/null && echo $ip is up & done wait echo "END" [root@controller ccx]# sh ping2.sh 192.168.198.129 is up 192.168.198.130 is up END [root@controller ccx]#

如上代码,使用多并发形式执行的,6个ip地址执行花了约5秒,运行过程较普通脚本快了差不多6倍!

多命令使用(有依赖关系)

多命令就是说这个脚本输出结果时有多条命令相互依赖,我们需要把这些相互依赖的命令放在{}中,然后在反括号后面加上并发符号}&。 一般像需要用到中括号的都是有for循环的,放{}的时候一定要放在循环语句里面,否则就无效,执行原理就是{}外面的for是一个父程序,而循环中的语句属于子程序,我们{}起来的是子程序,让子程序进入后台同步运行,需要注意的是,当需要并行执行的命令数量特别多,特别是所执行的命令占用的系统资源非常多时,可能会将整个系统的资源全部耗尽,影响其它程序的运行,这是并发执行的缺陷!

普通脚本

我对上述ping代码做过改进,我把ping得通和ping不通的都打印到屏幕上(这样代码间就出现了依赖关系,需要使用到{}。 ps:实现原理,如果ping不通的是以From开头,根据此原理,我们可以用awk筛选出是否有From,如果有就是ping不通的,如果没有,便为ping得通的,最后再用if判断awk筛选的是字符串是否为0即可。 代码和执行结果如下:

[root@controller ccx]# cat ping.sh #!/bin/bash # for i in {128..133};do ip="192.168.198.$i" ping=`ping -c 2 $ip |grep From| awk '{print $1}'` #echo $ping #下面if是判断字符为空 if [ "$ping" = "" ] ; then echo 192.168.198.$i is up echo 192.168.198.$i is up >> /root/ccx/up.log else echo 192.168.198.$i not up echo 192.168.198.$i not up >> /root/ccx/notup.log fi done echo "END" [root@controller ccx]# [root@controller ccx]# [root@controller ccx]# sh ping.sh 192.168.198.128 not up 192.168.198.129 is up 192.168.198.130 is up 192.168.198.131 not up 192.168.198.132 not up 192.168.198.133 not up END [root@controller ccx]# cat up.log 192.168.198.129 is up 192.168.198.130 is up [root@controller ccx]# cat notup.log 192.168.198.128 not up 192.168.198.131 not up 192.168.198.132 not up 192.168.198.133 not up [root@controller ccx]#

如上代码,实现过程是一条一条依次出现的,所以上面6个IP花了大概30秒,如果60个600个IP花的时间可想而知。

多并发脚本

我们对上述代码进行改进,用中括号把依赖代码括起来,然后加上&和wait,代码和执行结果如下:

[root@controller ccx]# cat ping.sh #!/bin/bash # for i in {128..133};do #注意,我的括号是放在for循环里面的,一定要放在循环里面,放外面无效! { ip="192.168.198.$i" ping=`ping -c 2 $ip |grep From| awk '{print $1}'` #echo $ping if [ "$ping" = "" ] ; then echo 192.168.198.$i is up echo 192.168.198.$i is up >> /root/ccx/up.log else echo 192.168.198.$i not up echo 192.168.198.$i not up >> /root/ccx/notup.log fi #中括号结束位置也是在for循环里面 }& done wait echo "END" [root@controller ccx]# sh ping.sh #运行结果分2次一次性弹出。 192.168.198.129 is up 192.168.198.130 is up 192.168.198.128 not up 192.168.198.132 not up 192.168.198.133 not up 192.168.198.131 not up END [root@controller ccx]#

如上代码执行结果是无序的,执行过程约5秒左右,速度大大提升。

最新回复(0)