在第一列中选择 IP 范围



我是Bash的新手,遇到了一个问题。我得到了以下输出:

159.180.30.159 BVI19987
159.180.30.160 BVI19987
159.180.30.161 BVI19987
159.180.30.162 BVI19987
159.180.30.163 BVI19987
159.180.30.164 BVI19987
159.180.30.165 BVI19987
159.180.30.166 BVI19987
159.180.30.167 BVI19987
159.180.30.168 BVI19987
159.180.30.170 BVI19987
159.180.30.171 BVI19987
159.180.30.172 BVI19987
159.180.30.173 BVI19987
159.180.30.174 BVI19987
159.180.30.175 BVI19987
159.180.30.176 BVI19987
159.180.30.177 BVI19987
159.180.30.178 BVI19987
159.180.30.179 BVI19987
159.180.30.180 BVI19987
159.180.30.181 BVI19987
159.180.30.182 BVI19987
159.180.30.183 BVI19987
159.180.30.184 BVI19987
159.180.30.185 BVI19987
159.180.30.186 BVI19987
159.180.30.187 BVI19987

只选择167到180之间的ip,并得到这个输出,最简单的方法是什么?

159.180.30.167 BVI19987
159.180.30.168 BVI19987
159.180.30.170 BVI19987
159.180.30.171 BVI19987
159.180.30.172 BVI19987
159.180.30.173 BVI19987
159.180.30.174 BVI19987
159.180.30.175 BVI19987
159.180.30.176 BVI19987
159.180.30.177 BVI19987
159.180.30.178 BVI19987
159.180.30.179 BVI19987
159.180.30.180 BVI19987

正则表达式对于范围不是很好。你可以硬编码一个像

grep -E '^159.180.30.1(6[7-9]|7[0-9]|80)' file
在这种情况下,更好的方法是使用带有自定义分隔符的Awk。
awk -F '.' '($4+0) >= 167 && ($4+0) <= 180' file

将0添加到字段强制它是一个纯数字,以避免Awk对字符串应用词法排序顺序的行为(其中17"大于")。167年).

显然可以添加更多条件,如

awk -F '.' '$1 == 159 && $2 == 180 && $3 == 30 && ($4+0) >= 167 && ($4+0) <= 180' file

或添加像

这样的正则表达式
awk -F '.' '/^159.180.30./ && ($4+0) >= 167 && ($4+0) <= 180' file

当你有一个更复杂的范围时,比如a/21,它从一个个位数网络开始,但跨越了一个双位数网络的边界,你可能最好使用一个理解符号的工具。通过一些简单的Python脚本,可以找到例如IPAddress或CIDR块匹配的正则表达式。

就像这样,直接的(希望不尴尬)方式:

awk -F'[ .]' '167 <= $4 && $4 <= 180' file
  • -F[ .].(空格)字符上分割列
  • $4是第四个字段

纯Bash:

while IFS= read -r line; do
IFS=$'. tn' read -ra fields <<< "$line"
((fields[3] >= 167 && fields[3] <= 180)) && printf '%sn' "$line"
done < input_file

awk中(更短且具有"更通用"的空白匹配):

awk -F'[[:space:].]' '$4 >= 167 && $4 <= 180' < /tmp/input
(旁注:这种方法伤害了互联网,应该避免;如果不能避免使用IPv4,请考虑使用纯十六进制形式的IPv4映射IPv6地址,以获得更广泛的兼容性。

要正确解析IPv4,最好使用正确的解析器,这里是Perl:

perl -F's|.' -MRegexp::Common -lane '
print if /^$RE{net}{IPv4}/ && $F[3] >= 157 && $F[3] <= 180
' file

检查perldoc Regexp::Common

一个简单的sed程序可以做到这一点:

sed -n -E '/^([0-9]{1,3}.){3}167 /,/^([0-9]{1,3}.){3}180 /p'

它打印(p)从匹配第一个正则表达式(([0-9]{1,3}.){3}167)开始的每一行,直到并包括匹配第二个正则表达式的那一行。

正则表达式匹配三组({3}),每组1到3位数字([0-9]{1,3}),每个数字后面跟着一个点(.),后面跟着167(或180),最后一个空格。如果您的输入使用制表符分隔列,请使用t而不是空格()。或者干脆放弃。

-n选项告诉sed不打印每个输入行(默认情况下是这样的)。p命令打印匹配的行。

-E选项告诉sed使用现代正则表达式。默认情况下,sed使用基本正则表达式;程序也可以使用它们来表示,但这会更冗长,因为基本正则表达式更简单,并且不支持正则表达式语法的一些新添加(好吧,它们是"new")30年前,但sed比它大20年。

如果输入包含多个范围的IP地址,并且您只想要以159.180.30.开头的IP地址,那么将([0-9]{1,3}.){3}替换为更具体的表达式:

sed -n -E '/^159.180.30.167 /,/^159.180.30.180 /p'

<<h3>评论/h3>上面的代码并不关心一组数字和点是否代表有效地址或其他任何东西。它依赖于要输出的行是连续的这一事实。它不做任何数值比较,也不理解170是在167180之间。它只关心起始行和结束行。

如果对行进行洗牌,可能不会产生预期的结果!

最新更新