Регулярное выражение для разбора конфигурации сетевого интерфейса

Мне интересно, если проблема здесь может быть решена с помощью одного регулярного выражения, или я должен сделать стандартный цикл и оценивать строку за строкой,

когда я запускаю включенный код, я получаю ['Ethernet0/22', 'Ethernet0/24']только результат должен быть ['Ethernet0/23', 'Ethernet0/25'],

любой совет по этому поводу?

 import re

 txt='''#
 interface Ethernet0/22
  stp disable
  broadcast-suppression 5
  mac-address max-mac-count 1
  port access vlan 452
 #
 interface Ethernet0/23
  stp disable
  description BTO
  broadcast-suppression 5
  port access vlan 2421
 #
 interface Ethernet0/24
  stp disable
  description Avaya G700
  broadcast-suppression 5
  port access vlan 452
 #
 interface Ethernet0/25
  stp disable
  description BTO
  broadcast-suppression 5
  port access vlan 2421
 #
 '''

 re1 = '''^interface (.*?$).*?BTO.*?^#$'''

 rg = re.compile(re1,re.IGNORECASE|re.DOTALL|re.MULTILINE)
 m = rg.findall(txt)
 if m:
  print m

4 ответа

Решение

Ваша проблема в том, что регулярное выражение продолжает находить BTO в следующей группе. В качестве быстрого обходного пути вы можете просто запретить символ "#" в идентификаторе интерфейса (при условии, что это недопустимо в записях, а только разделяет их).

re1 = '''^interface ([^#]*?$)[^#]*?BTO.*?^#$'''

Вот небольшой синтаксический анализатор для вашего файла. Это не только показывает решение вашей непосредственной проблемы, но и анализатор предоставляет вам хороший набор объектов, которые вы можете использовать для легкого доступа к данным в каждом интерфейсе.

Вот парсер:

from pyparsing import *

# set up the parser
comment = "#" + Optional(restOfLine)
keyname = Word(alphas,alphanums+'-')
value = Combine(empty + SkipTo(LineEnd() | comment))
INTERFACE = Keyword("interface")
interfaceDef = Group(INTERFACE + value("name") + \
    Dict(OneOrMore(Group(~INTERFACE + keyname + value))))

# ignore comments (could be anywhere)
interfaceDef.ignore(comment)

# parse the source text
ifcdata = OneOrMore(interfaceDef).parseString(txt)

Теперь, как его использовать:

# use dump() to list all of the named fields created at parse time
for ifc in ifcdata:
    print ifc.dump()

# first the answer to the OP's question
print [ifc.name for ifc in ifcdata if ifc.description == "BTO"]

# how to access fields that are not legal Python identifiers
print [(ifc.name,ifc['broadcast-suppression']) for ifc in ifcdata 
    if 'broadcast-suppression' in ifc]

# using names to index into a mapping with string interpolation
print ', '.join(["(%(name)s, '%(port)s')" % ifc for ifc in ifcdata ])

Распечатывает:

['interface', 'Ethernet0/22', ['stp', 'disable'], ['broadcast-suppression', '5'], ['mac-address', 'max-mac-count 1'], ['port', 'access vlan 452']]
- broadcast-suppression: 5
- mac-address: max-mac-count 1
- name: Ethernet0/22
- port: access vlan 452
- stp: disable
['interface', 'Ethernet0/23', ['stp', 'disable'], ['description', 'BTO'], ['broadcast-suppression', '5'], ['port', 'access vlan 2421']]
- broadcast-suppression: 5
- description: BTO
- name: Ethernet0/23
- port: access vlan 2421
- stp: disable
['interface', 'Ethernet0/24', ['stp', 'disable'], ['description', 'Avaya G700'], ['broadcast-suppression', '5'], ['port', 'access vlan 452']]
- broadcast-suppression: 5
- description: Avaya G700
- name: Ethernet0/24
- port: access vlan 452
- stp: disable
['interface', 'Ethernet0/25', ['stp', 'disable'], ['description', 'BTO'], ['broadcast-suppression', '5'], ['port', 'access vlan 2421']]
- broadcast-suppression: 5
- description: BTO
- name: Ethernet0/25
- port: access vlan 2421
- stp: disable
['Ethernet0/23', 'Ethernet0/25']
[('Ethernet0/22', '5'), ('Ethernet0/23', '5'), ('Ethernet0/24', '5'), ('Ethernet0/25', '5')]
(Ethernet0/22, 'access vlan 452'), (Ethernet0/23, 'access vlan 2421'), (Ethernet0/24, 'access vlan 452'), (Ethernet0/25, 'access vlan 2421')

Пример без регулярных выражений:

print [ stanza.split()[0]
        for stanza in txt.split("interface ")
        if stanza.lower().startswith( "ethernet" )
        and stanza.lower().find("bto") > -1 ]

Объяснение:

Я считаю, что композиции лучше всего читать "наизнанку":

for stanza in txt.split("interface ")

Разделите текст на каждое вхождение "интерфейса" (включая следующее пространство). Результирующая строфа будет выглядеть так:

Ethernet0/22
 stp disable
 broadcast-suppression 5
 mac-address max-mac-count 1
 port access vlan 452
#

Далее отфильтруйте строфы:

if stanza.lower().startswith( "ethernet" ) and stanza.lower().find("bto") > -1

Это должно быть само за себя.

stanza.split()[0]

Разделите разделы mathing на пустое пространство и возьмите первый элемент в результирующий список. Это в паре с фильтром startswith помешает IndexErrors

Вместо того, чтобы пытаться сделать паттерн между якорями ^ и $ и полагаться на #, вы можете использовать символы новой строки, разбивающие "сублинии" внутри совпадения одного блока.

например, определить предложения в терминах последовательности буквальных не-новых строк, ведущих к новой строке.

что-то вроде

 re1 = '''\ninterface ([^\n]+?)\n[^\n]+?\n[^\n]+BTO\n'''

будет производить результат, который вы после, из предоставленного исходного текста.

Другие вопросы по тегам