简介
IP 地址(Internet Protocol Address)是互联网协议地址的简称,是互联网上为联网的设备(如计算机、服务器、路由器、手机等)分配的唯一标识符。IP 地址的主要功能是实现不同网络设备之间的通信,确保数据包能够准确无误地从源地址传输到目标地址。
IP 地址如同网络世界的门牌号码,为每个联网设备提供独一无二的身份标识。通过 IP 地址,数据包可以在全球范围的互联网中找到确切的目的地。IP 地址是 TCP/IP 协议栈中网络层的核心要素,为上层协议(如 TCP、UDP、HTTP、SSH 等)提供服务,实现应用程序间的网络通信。
存在两种主要版本的 IP 地址,IPv4 是最广泛使用的版本,采用 32 位二进制数表示,通常写为点分十进制形式,如 192.168.0.1
。IPv6 是为应对 IPv4 地址耗尽而设计的新一代地址体系,使用 128 位二进制数表示,通常写为冒号分隔的十六进制数,如 2001:0db8:85c3:0000:0000:8a5e:0370:7339
。人们说的 IP 地址通常是指 IPv4 地址。
问题
在运维工作中,一种常见需求是统计文件中 ip 地址的数量,比如统计服务器上指定日志文件中的 ip 数量。
那么如何用 shell 命令来完成这个任务呢?
回答
要使用 Bash 命令统计文本文件中 IP 地址串的数量,可以拆分为两个步骤:
- 使用 grep 配合正则表达式筛选 IP 地址:
使用
grep
命令配合能够匹配 IPv4 地址的正则表达式,从文本文件中筛选出所有 IP 地址。 - 计数筛选结果行数:
利用
wc -l
命令计算上一步筛选结果的行数,即可得到 IP 地址的总数。
将这两步结合在一起,起初想到的命令如下:
代码语言:javascript复制grep -oE 'b([0-9]{1,3}.){3}[0-9]{1,3}b' ip-addresses.txt | wc -l
命令解析:
grep
:b
:单词边界,确保 IP 地址前后没有其他字符干扰。([0-9]{1,3}.){3}
:匹配连续三次的数字(1 到 3 位)后面跟着一个点号,即 IP 地址的前三段。[0-9]{1,3}
:匹配最后一段数字(1 到 3 位)。- 整个正则表达式目的是匹配点分十进制形式的 ip 地址。
-o
:只输出匹配到的部分,每一部分单独占一行。-E
:使用扩展正则表达式(ERE),以便我们可以使用更简洁的正则写法。'b([0-9]{1,3}.){3}[0-9]{1,3}b'
:这是匹配 IPv4 地址的正则表达式,解释如下:
ip-addresses.txt
:被统计的文件名,使用时替换为待统计 IP 地址的文本文件的实际路径。wc -l
:wc
是 Word Count (词数统计)命令,用于计算行数、单词数、字符数等。-l
选项指定只计算行数,在这里就是统计出 IP 地址的个数。
执行上述命令后,Bash 会输出 ip-addresses.txt
文件中 IP 地址的总数。
我们编写一个待测试文件,内容如下:
代码语言:javascript复制192.168.1.27
0.0.0.0 1.22.3.123 1.234.34.0 123.45.6.78
address is 23.5.5.5
_2.3.4.5_
192.168
172.18.19
123.4.12.259 22.333.0.100
1.22.233.4444
abc1.2.5.200 2.3.44.55ef
文件中符合预期的 ip 地址数量是 6。
使用上述命令测试,输出结果是 8,
分析可知上述命令将 123.4.12.259
22.333.0.100
这两个字符串判断为 ip 地址了。
如何修改命令中的正则表达式来避免这个错误呢?
我们知道,点分十进制形式的 ip 地址由三个点号分隔的四个十进制数组成,其中每个十进制的有效范围是 0~255。 经过搜索学习,笔者将命令改为
代码语言:javascript复制grep -oP 'b(([01]?dd?|2[0-4]d|25[0-5]).){3}([01]?dd?|2[0-4]d|25[0-5])b' ip-addresses.txt` | wc -l
-P
: 选项启用 Perl 兼容的正则表达式(PCRE)。相比基本正则表达式,PCRE 提供了更丰富的功能和更灵活的匹配规则。'b(([01]?dd?|2[0-4]d|25[0-5]).){3}([01]?dd?|2[0-4]d|25[0-5])b'
: 这是用于匹配 IPv4 地址的 Perl 兼容正则表达式。解释如下:([01]?dd?|2[0-4]d|25[0-5])
: 三种可能的 IPv4 段值的组合,确保值在 0 到 255 之间。具体解释如下:.
: 匹配点号,作为十进制数之间的分隔符。[01]?dd?
: 匹配 0 到 199 之间的数,可以是一位、两位或三位数,这个模式如果匹配三位数则第一位只能是 0 或 1。2[0-4]d
: 匹配 200 到 249 之间的数,第一位固定是 2,第二位是 0 到 4 之间的数,第三位是任何数字。25[0-5]
: 匹配 250 到 255 之间的数,第一位固定是 2,第二位是 5,第三位是 0 到 5 之间的数。|
: 上述三个子模式被竖线分开,表示“或”的关系。(([01]?dd?|2[0-4]d|25[0-5]).){3}
: 重复 3 次的子模式,用于匹配 IP 地址的前三段。子模式内部结构如下:([01]?dd?|2[0-4]d|25[0-5])
: 类似于前面的子模式,用于匹配 IPv4 地址的最后一段,确保其值在 0 到 255 之间。
再进行测试,输出结果是 6,符合预期。
可见经过改进后的命令能够精确匹配合法的 IP 地址,避免匹配到诸如 123.4.12.259
这样的无效地址。
参考:
- man grep
- man pcre