我试图从ifconfig(ubuntu)解析信息。通常,我会将这样的数据块分成单词,然后搜索子字符串以获得我想要的内容。例如,给定line = "inet addr:192.168.98.157 Bcast:192.168.98.255 Mask:255.255.255.0"
,并寻找广播地址,我会这样做:
for word in line.split():
if word.startswith('Bcast'):
print word.split(':')[-1]
>>>192.168.98.255
但是,我觉得有时间开始学习如何使用正则表达式来完成这样的任务。到目前为止,这是我的代码。我已经破解了几种模式(inet addr,Bcast,Mask)。代码后的问题......
# git clone git://gist.github.com/1586034.git gist-1586034
import re
import json
ifconfig = """
eth0 Link encap:Ethernet HWaddr 08:00:27:3a:ab:47
inet addr:192.168.98.157 Bcast:192.168.98.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe3a:ab47/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:189059 errors:0 dropped:0 overruns:0 frame:0
TX packets:104380 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:74213981 (74.2 MB) TX bytes:15350131 (15.3 MB)\n\n
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:389611 errors:0 dropped:0 overruns:0 frame:0
TX packets:389611 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:81962238 (81.9 MB) TX bytes:81962238 (81.9 MB)
"""
for paragraph in ifconfig.split('\n\n'):
info = {
'eth_port': '',
'ip_address': '',
'broadcast_address': '',
'mac_address': '',
'net_mask': '',
'up': False,
'running': False,
'broadcast': False,
'multicast': False,
}
if 'BROADCAST' in paragraph:
info['broadcast'] = True
if 'MULTICAST' in paragraph:
info['multicast'] = True
if 'UP' in paragraph:
info['up'] = True
if 'RUNNING' in paragraph:
info['running'] = True
ip = re.search( r'inet addr:[^\s]+', paragraph )
if ip:
info['ip_address'] = ip.group().split(':')[-1]
bcast = re.search( r'Bcast:[^\s]+', paragraph )
if bcast:
info['broadcast_address'] = bcast.group().split(':')[-1]
mask = re.search( r'Mask:[^\s]+', paragraph )
if mask:
info['net_mask'] = mask.group().split(':')[-1]
print paragraph
print json.dumps(info, indent=4)
以下是我的问题:
我是否已采用最佳方法处理已实施的模式?我可以抓住地址而不拆分':'然后选择最后一个数组。?
我坚持使用HWaddr。什么是匹配这个mac地址的模式?
编辑:
好的,所以这就是我最终解决这个问题的方法。我开始试图在没有正则表达式的情况下试图解决这个问题...只是操纵蜇和列表。但事实证明这是一场噩梦。例如,HWaddr
与其地址的区别是space
。现在将inet addr
与其:
的地址分开。它是一个完全的婊子,用这样的不同分离器刮掉。不仅是代码的婊子,也是阅读的婊子。
所以,我用正则表达式做了这个。我认为这是何时使用正则表达式的强有力的理由。
# git clone git://gist.github.com/1586034.git gist-1586034
# USAGE: pipe ifconfig into script. ie "ifconfig | python pyifconfig.py"
# output is a list of json datastructures
import sys
import re
import json
ifconfig = sys.stdin.read()
print 'STARTINPUT'
print ifconfig
print 'ENDINPUT'
def extract(input):
mo = re.search(r'^(?P<interface>eth\d+|eth\d+:\d+)\s+' +
r'Link encap:(?P<link_encap>\S+)\s+' +
r'(HWaddr\s+(?P<hardware_address>\S+))?' +
r'(\s+inet addr:(?P<ip_address>\S+))?' +
r'(\s+Bcast:(?P<broadcast_address>\S+)\s+)?' +
r'(Mask:(?P<net_mask>\S+)\s+)?',
input, re.MULTILINE )
if mo:
info = mo.groupdict('')
info['running'] = False
info['up'] = False
info['multicast'] = False
info['broadcast'] = False
if 'RUNNING' in input:
info['running'] = True
if 'UP' in input:
info['up'] = True
if 'BROADCAST' in input:
info['broadcast'] = True
if 'MULTICAST' in input:
info['multicast'] = True
return info
return {}
interfaces = [ extract(interface) for interface in ifconfig.split('\n\n') if interface.strip() ]
print json.dumps(interfaces, indent=4)
答案 0 :(得分:13)
而不是重新发明轮子:
或者,如果您想要一个可在多个平台上运行的便携式版本..
答案 1 :(得分:4)
我是否已采用最佳方法处理已实施的模式?我可以抓取地址而不拆分':'然后选择最后一个数组。?
虽然[^\s]
等同于\S
,但您的模式可以正常运行。
您可以通过将地址放入捕获组来抓取地址而不拆分“:”,如下所示:
ip = re.search(r'inet addr:(\S+)', paragraph)
if ip:
info['ip_address'] = ip.group(1)
如果您有正则表达式的更多分组部分,您可以按照它们在正则表达式中出现的顺序来引用它们,从1开始。
我被困在HWaddr身上。什么是匹配这个mac地址的模式?
现在你知道了分组,你可以像其他地址一样获得HWaddr:
mac = re.search(r'HWaddr\s+(\S+)', paragraph)
if mac:
info['mac_address'] = mac.group(1)
请注意,使用更高级的正则表达式,您实际上可以同时执行其中的多个步骤。例如,这里有一个示例正则表达式,它一步拔出接口名称,IP地址和网络掩码:
>>> re.findall(r'^(\S+).*?inet addr:(\S+).*?Mask:(\S+)', ifconfig, re.S | re.M)
[('eth0', '192.168.98.157', '255.255.255.0'), ('lo', '127.0.0.1', '255.0.0.0')]
答案 2 :(得分:2)
老实说,正则表达式并不比简单的字符串操作特别好;如果有的话,他们总是慢一点。
这就是说,你应该开始用更好的分割来清理你的输入:
lines = [line.strip() for line in ifconfig.split("\n") if line.strip() != '']
这将删除行周围的所有空格,并丢弃空的空格;您的正则表达式现在可以从^
开始,以$
结束,这将减少误报的可能性。
然后你真的要看分组;你正在使用的模式只是荣耀startswith
,当然不如startswith
优化。一个正则表达式的大师会提出更好的,但是例如HWAddr线的简单模式将是
>>> m = re.match(r'^([A-z]*\d)\s+(Link)\s+(encap):([A-z]*)\s+(HWaddr)\s+([A-z0-9:]*)$',lines[0])
>>> m.groups()
('eth0', 'Link', 'encap', 'Ethernet', 'HWaddr', '08:00:27:3a:ab:47')
但实际上,我看得越多,基于split()和split(':')的简单方法对于这种严格格式化的输入就越有意义。正则表达式使您的代码可读性降低,而且非常昂贵。正如JWZ曾经说过的那样,“有些人在面对问题时会想'我知道,我会使用正则表达式'。现在他们有两个问题。“
答案 3 :(得分:2)
使用新的3.0.0版本的psutil(https://github.com/giampaolo/psutil),您可以避免解析ifconfig输出并直接在Python中执行此操作: http://pythonhosted.org/psutil/#psutil.net_if_addrs
>>> import psutil
>>> psutil.net_if_addrs()
{'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
>>>
答案 4 :(得分:1)
尝试一下:
>>> import re
>>> m = re.search(r'^(?P<interface>eth\d+|eth\d+:\d+|lo|ppp\d+)\s+' +
... r'Link encap:(?P<link_encap>\S+)\s+' +
... r'HWaddr\s(?P<hardware_address>[0-9a-f]{2}:[0-9a-f]{2}:' +
... r'[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})\s',
... ifconfig,
... re.MULTILINE
... )
>>> m.groupdict()
{'hardware_address': '08:00:27:3a:ab:47',
'interface': 'eth0',
'link_encap': 'Ethernet'}