上一期我们分享了正则解析的OneTake方式,比如解析mac、arp等。其实我们也可以解析version、型号、系列等单个指标信息。在全文里一次查找,都不需要将文本分行切割。
今天我们分享一种解析的思路——分而治之,带大家一起边讲边写,希望大家跟着我的思路走。
这种主要是解析show interface这种配置
端口配置的特点是:
1、信息是按段分开的
2、每段里面含有大量的信息
3、每段信息里有些字段是一定有的,有些是可能有可能无的
我们的思路就是:
1、先找特点,将配置信息分成一段一段的文本
2、根据要取的信息特点写正则
3、去每一段文本里匹配正则提取信息,如果无则置空字段
我们开始写一段解析端口的配置。
1、首先我们先切割文本段
把每个端口的信息放到一个字符串里处理。
确定换行符是很重要的一个环节,我们用这段代码把换行符给找出来。
import re
with open('show_interface.log','r',encoding='utf8') as f:
log = f.read()
print(log.encode('utf8'))
有时候设备的换行符是“rn”,这个里面是”n”(说实话底层我也不太清楚,但是我知道Linux和windows对于换行是有区别的,与采集的设备和被采集的设备可能都有关系。建议初期大家都去看换行是什么。后期写的代码我很少通过本地直接采集设备,而是通过一个自己封装的API去ssh到设备采集信息,所以返回的格式比较统一,但是偶尔有时候也会出现有的设备返回的是rn,有的返回的是rn,所以具体问题具体分析,同时我会在处理正则的时候全局把换行统一一下)
从上面的截图我们发现,端口的信息总是端口名称 is 开头,两个换行结束,所以我们用正则去把所需要的全部端口文本先筛选出来。
import re
with open('show_interface.log','r',encoding='utf8') as f:
log = f.read()
intf_info_re = re.compile(r'(?:Ethernetd+/d+|Vland+|mgmtd+)s+is[sS]+?nn')
intf_info_texts = intf_info_re.findall(log)
for i in intf_info_texts:
print(i,end='以上是一个端口信息n')
这里面还是有三点要说,
“(?:一段正则|)”,两个部分说,圆括号代表的是优先级,这几个端口类型是或的关系“|”,为了防止下面这种情况我们把端口类型几个用圆括号给圈起来
圆括号表示是一个整体,同时默认代表的是提取这个串。结果如果不加“?:”,我们提取的结果是端口名称,而没有提取整个端口的信息,有两种做法,一种是用“?:”代表识别正则但是不提取子串,所以findall是以整体去提出每个端口的配置信息。
第二种方式是把整个串再用圆括号圈起来,那findall会识别第一个子串和整体这个子串,返回的是tuple的列表。
我不太推荐第二种方法。
然后是findall的用法,之前正则也讲过,就是返回所有匹配的数据列表,数据可能是字符串可能是tuple等。大家尽量用第一种方法,返回的就是字符串了。
还有一个点就是我用了“[sS]+?”端口开头和两个换行中间的部分,同时用的是非贪婪,这样防止它把中间其他端口贪婪的也识别了。
2、文本内编写OneTake,提取重要信息
接下来做的就很简单了,在每个提取的文本段内(字符串)提取我们想要的端口信息就可以了,你可以写正则,甚至用字符串的一些find、index等方法提取,用最省力的。这段无疑用正则一个个去解析最合适。
我们打个样,在文本内去解析端口名和状态以及端口描述,其他的大家可以自己练习以下。
先写端口的,其实刚才我们已经写好了端口的正则了
intf_name_re = re.compile(r'Ethernetd+/d+|Vland+|mgmtd+',re.I)
intf_protocol_status_re = re.compile( r'd+siss([a-zA-Z]+)',re.I)
intf_desc_re = re.compile( r'Description:([sS]+?)n',re.I)
- 端口的没什么好说的。后面加了re.I代表的是忽略大小写。
- 状态的找规律即可,前面是端口号数字空格然后是is,紧接着是空格,后面是up、down,防止以后出幺蛾子,我们用字母框都匹配。这个地方设计到数据建模问题,我们的每个字段后期一定是我们的一个数据表,数据结构,所以前期每个字段都要斟酌,比如状态是分协议状态和配置状态的。
- 描述的我们中间用空格非空格非贪婪一下匹配到换行结束,简单粗暴。
上代码和结果
可以看到拿到了端口的信息表,还有很多字段大家可以按需去添加。
有几点说明:
- 我们用了search,注意它和match的区别
- 我们取字段的时候group的一些操作
- 注意我的缩进,变量命名方式
- 我们预先赋值字段,防止到时候引用发现没有定义
以上就是今天的分享,今天的正则,我变着花的给大家写,希望尽量带入一些知识点,这些花儿也是必要的。后续还会继续分享一些TIPS。
希望大家体会我在写这个正则时候的思路,顺序。这是我们写了这么久的一个经验总结,最佳实践~可以少走一些弯路。
最后奉上今天的主体代码,希望大家点赞、分享、订阅、喜欢、在看~
下一篇文章,我打算写写非技术的,胡吹乱侃一些对NetDevOps的看法。大家在技术上有什么难题也欢迎与我分享,我也许可以提供一些思路~
同时在考虑 要不要来一个集赞写正则的活动:)
import re
intf_name_re = re.compile(r'Ethernetd+/d+|Vland+|mgmtd+',re.I)
intf_protocol_status_re = re.compile( r'd+siss([a-zA-Z]+)',re.I)
intf_desc_re = re.compile( r'Description:([sS]+?)n',re.I)
with open('show_interface.log','r',encoding='utf8') as f:
intfs = []
log = f.read()
intf_info_re = re.compile(r'(?:Ethernetd+/d+|Vland+|mgmtd+)s+is[sS]+?nn')
intf_info_texts = intf_info_re.findall(log)
for i in intf_info_texts:
intf_name = None
intf_protocol_status = None
intf_desc = None
intf_name_match = intf_name_re.search(i)
intf_protocol_status_match = intf_protocol_status_re.search(i)
intf_desc_match = intf_desc_re.search(i)
if intf_name_match:
intf_name = intf_name_match.group()
if intf_protocol_status_match:
intf_protocol_status = intf_protocol_status_match.group(1)
intf_protocol_status = intf_protocol_status.strip()
if intf_desc_match:
intf_desc = intf_desc_match.group(1)
intf_desc = intf_desc.strip()
intfs.append({
'intf_name':intf_name,
'intf_protocol_status':intf_protocol_status,
'intf_desc':intf_desc
})
for intf in intfs:
print(intf)
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
Spring是什么? Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 Spring的优点 通过控制反转和依赖注入实现松耦合。 支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 通过切面和模板减少样板式代码。 声明式事务的支持…