用shell编程批量测试一组ip地址是否可用

前情提要

昨天不是在把服务器虚拟化了吗,然后又到了装虚拟机操作系统的阶段。因为公司的局域网IP地址可能在频繁变化,所以无论何时都不能确定哪些IP地址没被占用,于是快速知道哪些IP可用成为了一件亟需解决的事情。

所以就想着自己写一个自动化脚本来实现这个需求,于是周日一天就干这个事了。

我先是把需求在网上搜了一下,看看有没有现成的例子可以借鉴。发现还真有类似的脚本,但是不能拿来直接用。正好我也想学一下shell编程,趁这个机会边做边学,岂不美哉!

分解需求

于是我把我的大需求分解为了几个小需求,比如:

  • shell中怎么读取控制台输入?

  • shell中怎么进行字符串的截取和分割?

  • shell中怎么进行算术运算?

  • shell中怎么使用for循环?

  • for循环中的范围怎么确定?

  • shell中怎么使用条件判断?

  • if语句中判断条件表达式怎么写?

等等,这些问题只要有c语言编程基础的人就会自然而然地想到。在这个学习的过程中,我也体会到了一些编程通常都会使用到的功能点,包括如上所提的几个问题。也就是说,一些功能是大部分编程语言都要具备的,所以,学会了一门语言,再去学其他语言将会很快。

小坑防入

另外就是我把我在学习的过程中爬过的几个坑分享给大家,以免大家再走弯路。

  1. 变量之间的运算不能直接进行,需要通过命令。

    例如,你想把两个数相加的结果赋值给一个新变量,你不能这样写num=a+b

    而应该这样写:num=`expr $a + $b`

    等号右边是一个命令,expr表示把加号左右的变量相加,而且,变量与加号之间还要空格,不然就是把两个变量直接拼接起来。

  2. if语句的判断条件,if [ $ping -eq 100 ],这里的中括号要与内部的内容用空格隔开,不然就无法识别中括号里面的内容。

好了,各位看官可能看的不耐烦了,小的这就把代码敬上:

至尊代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#! /bin/bash
read -p "请输入起始IP地址:" ip0
ip1=${ip0%.*};ip2=${ip0##*.}
read -p "请输入要测试的连续IP地址个数:" num
num=`expr $ip2 + $num`
echo =====================
for ((ip2;ip2<num;ip2++))
do
loss=`ping -c 1 $ip1.$ip2 | grep loss | awk '{print $(NF-4)}' | awk -F"%" '{print $1}'`
if [ $loss -eq 100 ]; then
echo -e $ip1.$ip2 is "\E[1;31mDOWN\E[0m"
else
echo -e $ip1.$ip2 is "\E[1;32mUP\E[0m"
fi
done
关键一行

第9行是这个脚本中最关键的代码,这一行代码的意思就是把ping命令执行结果中的丢包率提取出来并赋值给loss这个变量。首先ping我们拼接好的IP地址,只不过在ping命令中加了一个参数-c 1,这个参数的意思就是ping的次数,此处设为1是为了加快测试的速度,以免在一个ping不通的IP地址上浪费太多时间。然后后面的目的就是把表示丢包率的百分比数字提取出来。于是先使用grep命令筛选出包含loss这一行,如果你在命令行窗口使用如下命令ping -c 1 192.100.1.123 | grep loss(IP地址可更改成你局域网里任意一个)就会输出类似这一行1 packets transmitted, 0 received, 100% packet loss, time 0ms。再使用awk命令,就像根据数组下标get数组元素一样,我们就可以把表示丢包率的百分比数字提取出来。awk命令的具体使用可以参考这篇文章akw用法入门

最后一问

除了上面提到的两个坑外,还有一个知识点是必须要提出来的,那就是关于字符串截取的问题。为什么会有字符串截取这种需求呢?那是因为在一长串字符串中,总有不变的部分和变化的部分,我们的目的就是把不变的部分和变化的部分分割出来,以便做不同的处理。比如本文所提到的测试连续IP的可用性的问题,其中对于ipv4类型的地址中就会有不变的部分和需要变化的部分,其中子网段就是变化的部分。我们需要把这部分提出来,以便进行加一操作。于是为了满足我们使子网段加一的操作,我们就需要集中火力来解决字符串截取的问题。鉴于本人目前知识的贫乏程度,还不能完全靠自己解决这个问题,所以还是拿出程序员的看家本领来:上网搜!

这是我搜到的答案:Linux Shell 截取字符串,这应该是最原始的内容了。其中使用了一些可能只有程序猿才能看懂的符号吧,反正不是人能看懂的。字符串截取的符号是类似这样的${var##*.}${var%.*},我看了看键盘#和%的位置,正好一个在美元符号$的左边,一个在其右边。又因为${var##*.}${var%.*}表示字符串截取的操作,所以,哪个符号在$左边就表示裁掉左边的,哪个符号在$右边就表示裁掉右边的,其中*号也表示裁掉的部分相对于分隔符的所在的位置。#或%的个数,表示裁剪的力度,数量为1力度就最轻,数量为2力度就最重。

比如我有一个IP地址ip=192.168.8.115,使用${ip##*.}就表示从左边第一个“.”开始裁至最后一个“.”左边的内容,剩下“115”这个字符串。使用${ip%.*},表示从右开始,删除第一个“.”右边的内容,剩下“192.168.8”这个字符串。

从这个例子我们可以看到,使用#或%的方法只适用于把一个字符串一分为二的操作,被裁掉剩下的部分不是头就是尾。但是如果我们需要提取一个字符串中间某一部分的内容怎么办呢,有没有什么工具可以办得到呢?值得庆幸的是,这在Linux的世界中是非常容易找到的,它就是awk。awk这个工具的学问可就大了,你难以想象得到这么一个小小的工具竟然能实现一些逻辑判断或者循环,它甚至是一门程序设计语言!在我上面的代码中,也可以看到我对这个强大工具的简单应用。

好了,以上这就是我对shell学习的一个过程,可以说是对后续的shell学习打好了基础吧。


用shell编程批量测试一组ip地址是否可用
https://www.chuckfang.com/2019/02/24/用shell编程批量测试一组ip地址是否可用/
作者
方程
发布于
2019年2月24日
许可协议