网络工程师Python正则表达式(re实验5,Finditer函数,列表推导式)
哈喽,大家好,我又来了。我们继续推进,这篇来一起学习finditer函数。这个函数非常适合用来解析行列报文回显,如“display interface interface brief”、“display ip interface brief”之类的。此外,本文后面还会体验一下列表推导式的魅力。话不多说,我们直接开始吧。
本文部分参考知乎专栏 @弈心 《网路行者》实验思想,推荐移步阅读。
本文部分参考书籍《Python for network engineers》,纯英文,推荐移步阅读。
本专栏简介及目录入口,如果您不知道从何读起,建议从这篇《目录》开始,连接如下:
Finditer函数概述
Finditer函数会根据正则规则,匹配后返回一个有match对象组成的可迭代对象(iter)。啥是可迭代对象呢?我自己的理解是你可以从某范围内,一个一个地把里面的东西给喊出来,这个范围就是一个可迭代对象。比如你钱包里有一大叠钞票,你可以把钞票一张一张的数出来,这钱包就是一个可迭代对象啦。这个比方可能不是很严谨,anyway,辅助理解咯。
另外值得提一下,根据该函数的使用说明,无论是否有匹配到,都会返回一个迭代器,而非None之类的。怎么查手册?如下。
>>> help(re.finditer)
Help on function finditer in module re:
finditer(pattern, string, flags=0)
Return an iterator over all non-overlapping matches in the
string. For each match, the iterator returns a Match object.
Empty matches are included in the result.
翻译一下么?来哩。
>>> help(re.finditer)
re模块中finditer函数的帮助
finditer(pattern, string, flags=0)
函数返回一个迭代器,这迭代器内容为字符串经正则规则处理命中的全部匹配项。匹配项与匹配项是不能重叠的。
每个匹配项都是一个Match对象。
如全部没匹配中,也是会返回迭代器。(只不过这个迭代器为空而已。)
一般我对一个没怎么用过或忘了怎么用的函数,我就在IDLE这么敲,于是快速看看怎么用或者是啥个意思。
如果这里您有点理解不了,没关系的,咱们跟着实验敲敲敲就知道了。
实验过程
第 1 步,解析display ip interface brief回显(常规方式)
建立一个文件夹。
output_from_cli为文本文件,内容如下:
*down: administratively down
^down: standby
(l): loopback
(s): spoofing
The number of interface that is UP in Physical is 3
The number of interface that is DOWN in Physical is 6
The number of interface that is UP in Protocol is 3
The number of interface that is DOWN in Protocol is 6
Interface IP Address/Mask Physical Protocol
LoopBack0 11.11.11.11/32 up up(s)
MEth0/0/1 unassigned down down
NULL0 unassigned up up(s)
Vlanif1 192.168.11.11/24 up up
Vlanif10 10.18.123.5/25 down down
Vlanif12 12.1.1.3/29 *down down
Vlanif34 34.1.1.5/24 down down
Vlanif172 172.18.123.5/26 *down down
Vlanif192 192.168.123.5/27 *down down
Python脚本文件,内容如下:
import re
with open('output_from_cli') as output:
# 整个文本读进来
output_text = output.read()
# 如必要可看一下读取的内容,格式是str
#print(output_text)
result = re.finditer(r'(\S+) +'
r'([\d./]+) +'
r'(up|down|\*down) +'
r'(up|down)',
output_text)
#如果写成一行可以这么写,上面分行的写法是为了更简洁好看一点而已,实际是等效的。
#result = re.finditer(r'(\S+) +([\d./]+) +(up|down|\*down) +(up|down)',output_text)
print(result)
print(type(result))
for match in result:
print(match)
代码和正则表达式的意思我就不讲了,前面的文章已经反复提及了。跑一下Python脚本,截图如下。
结合groups()等知识,我们就可以对子组进行捕获提取了。大家调整下尝试。
第 2 步,解析display ip interface brief回显(列表推导式)
怎么把代码写得又短又好看,让别人觉得有点高大上的感觉呢?哈哈哈,来……
import re
from pprint import pprint
with open('output_from_cli') as output:
regex = r'(\S+) +([\d./]+) +(up|down|\*down) +(up|down)'
result = [match.groups() for match in re.finditer(regex, output.read())]
pprint(result)
这个我们用到了列表推导式,会稍微高阶一点,如果基础稍微熟悉了以后可以尝试写点这样的,简洁效率也高。但是如果基础不是很扎实的,就容易绕着绕着给绕晕了,自行评估哈。
result = [match.groups() for match in re.finditer(regex, output.read())]
这句话解释一下
1、re.finditer(regex, output.read()),这里返回了迭代器(上一步的内容);
2、for match in ……,变量match在迭代其中挨个迭代,借助了for循环;
3、每次迭代,变量match又调用了方法groups();
4、处理后结果被中括号[]括起来,形成列表赋值给变量result。
短小精悍的一句代码,里面包含了很多知识点,需要反复品味。来,跑一下看看。
其实对于咱普通网工来说,啧啧啧~,也不用太炫技太追求效率啦。能实现目标才是关键,老老实实的写,多几行代码又何妨呢?
第 3 步,温故知新日志例子(用finditer函数)
早前,我们用search和match函数做了日志分析。那么,同样的日志文件,我们来体验一下finditer函数,算是温故知新吧。我学习正则表达式的经验就是得一次一次反复学习,反复练习哦。
建一个实验文件夹。日志内容,保存成log.txt。
Sep 26 2021 23:11:02-08:00 Layer3Switch-1 L2IFPPI/4/MFLPVLANALARM:OID 1.3.6.1.4.1.2011.5.25.160.3.7 MAC move detected, VlanId = 54, MacAddress = 0000-5e00-0136, Original-Port = GE0/0/1, Flapping port = GE0/0/2. Please check the network accessed to flapping port.
Sep 26 2021 23:11:08-08:00 Layer3Switch-1 L2IFPPI/4/MFLPVLANALARM:OID 1.3.6.1.4.1.2011.5.25.160.3.7 MAC move detected, VlanId = 54, MacAddress = 0000-5e00-0136, Original-Port = GE0/0/1, Flapping port = GE0/0/2. Please check the network accessed to flapping port.
Sep 26 2021 23:11:10-08:00 Layer3Switch-1 L2IFPPI/4/MFLPVLANALARM:OID 1.3.6.1.4.1.2011.5.25.160.3.7 MAC move detected, VlanId = 54, MacAddress = 0000-5e00-0136, Original-Port = GE0/0/2, Flapping port = GE0/0/3. Please check the network accessed to flapping port.
Sep 26 2021 23:11:15-08:00 Layer3Switch-1 L2IFPPI/4/MFLPVLANALARM:OID 1.3.6.1.4.1.2011.5.25.160.3.7 MAC move detected, VlanId = 54, MacAddress = 0000-5e00-0136, Original-Port = GE0/0/3, Flapping port = GE0/0/1. Please check the network accessed to flapping port.
Python代码内容,保存成lab5.py。
import re
regex = (r'.*VlanId = (\d+), '
r'MacAddress = \S+, '
r'Original-Port = (\S+), '
r'Flapping port = (\S+)\.')
ports = set()
with open('log.txt') as f:
for each_match in re.finditer(regex, f.read()):
vlan = each_match.group(1)
ports.add(each_match.group(2))
ports.add(each_match.group(3))
print('Loop between ports {} in VLAN {}'.format(', '.join(ports), vlan))
我们把代码调整成finditer函数。大家可以回过头对比一下search函数、match函数同样的日志解析例子。Finditer函数并不是逐行匹配,而是一次匹配,效率会高不少。同时,无论Finditer函数处理的结果如何,有无匹配中,其始终返回一个可迭代对象而不是None,因此也不用再做是否匹配得中的if判断了。我们试跑一下脚本看看吧。
实验总结
学点新的,复习旧的,再复习就的过程中再添点新的,一步一步往前拱,知识才得以巩固和拓展。除了Finditer函数的使用外,我们还点了一下列表推导式,这个如果学有余力想进阶的童鞋们可以继续深挖,Python中不仅有列表推导式,还有字典推导式等各种推导式。推导式除了简洁炫酷外,效率也比较高,但我始终觉得看起来有点绕。
最近思考,网络运维自动化我们用python拿着paramiko、netmiko、nornir等去连接设备采集信息和下发配置。大家想想,在现实生产环境中,有多少普通运维网工能自主地在大型生产网中找到点位部署、使用这些“联机”手段呢?网工自己写的未经严格测试的脚本代码能随随便便去操控设备呢?其实很难很难。真正的生产网都是分权分域有各种安全管控和审计的,这一点国内国外都如此的。我想,小型园区网的运维工程师、部分大型网络某些岗位的运维工程师是有条件尝试,但是最开始能从show或display等不影响设备配置的任务做起,逐步展开。然后,目前没机会实际开展网络运维自动化的网工呢?通过实验网或模拟器,掌握新技能,说不定哪天能用上呢?简历面试舔砖加瓦?处理这些我想更重要的是把握新趋势,理解自动化运维思想,把这种思想应用到日常的运维工作、事务工作、甚至日常生活。回到实际的工作,抛开“联机”问题,离线方面,网络运维自动化其实已经能做很多事情了,实施脚本制作,配置回显解析,配置核对,线上线下台账比对,资源管理核对等。好吧,先来学习掌握正则表达式吧哈哈。
感谢阅读,欢迎关注点赞收藏评论交流。
2021年11月于广东汕头