Флаги#

При использовании функций или создании скомпилированного регулярного выражения можно указывать дополнительные флаги, которые влияют на поведение регулярного выражения.

Модуль re поддерживает такие флаги (в скобках короткий вариант обозначения флага):

  • re.ASCII (re.A)

  • re.IGNORECASE (re.I)

  • re.MULTILINE (re.M)

  • re.DOTALL (re.S)

  • re.VERBOSE (re.X)

  • re.LOCALE (re.L)

  • re.DEBUG

В этом подразделе для примера рассматривается флаг re.DOTALL. Информация об остальных флагах доступна в документации.

re.DOTALL#

С помощью регулярных выражений можно работать и с многострочной строкой.

Например, из строки sh_cdp надо получить имя устройства, платформу и IOS:

In [2]: sh_cdp = '''
   ...: Device ID: SW2
   ...: Entry address(es):
   ...:   IP address: 10.1.1.2
   ...: Platform: cisco WS-C2960-8TC-L,  Capabilities: Switch IGMP
   ...: Interface: GigabitEthernet1/0/16,  Port ID (outgoing port): GigabitEthernet0/1
   ...: Holdtime : 164 sec
   ...:
   ...: Version :
   ...: Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(55)SE9, RELEASE SOFTWARE (fc1)
   ...: Technical Support: http://www.cisco.com/techsupport
   ...: Copyright (c) 1986-2014 by Cisco Systems, Inc.
   ...: Compiled Mon 03-Mar-14 22:53 by prod_rel_team
   ...:
   ...: advertisement version: 2
   ...: VTP Management Domain: ''
   ...: Native VLAN: 1
   ...: Duplex: full
   ...: Management address(es):
   ...:   IP address: 10.1.1.2
   ...: '''

Конечно, в этом случае можно разделить строку на части и работать с каждой строкой отдельно, но можно получить нужные данные и без разделения.

В этом выражении описаны строки с нужными данными:

In [3]: regex = r'Device ID: (\S+).+Platform: \w+ (\S+),.+Cisco IOS Software.+ Version (\S+),'

В таком случае, совпадения не будет, потому что по умолчанию точка означает любой символ, кроме перевода строки:

In [4]: print(re.search(regex, sh_cdp))
None

Изменить поведение по умолчанию, можно с помощью флага re.DOTALL:

In [5]: match = re.search(regex, sh_cdp, re.DOTALL)

In [6]: match.groups()
Out[6]: ('SW2', 'WS-C2960-8TC-L', '12.2(55)SE9')

Так как теперь в точку входит и перевод строки, комбинация .+ захватывает все, между нужными данными.

Теперь попробуем с помощью этого регулярного выражения, получить информацию про всех соседей из файла sh_cdp_neighbors_sw1.txt (вывод сокращен).

SW1#show cdp neighbors detail
-------------------------
Device ID: SW2
Entry address(es):
  IP address: 10.1.1.2
Platform: cisco WS-C2960-8TC-L,  Capabilities: Switch IGMP
Interface: GigabitEthernet1/0/16,  Port ID (outgoing port): GigabitEthernet0/1
Holdtime : 164 sec

Version :
Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(55)SE9, RELEASE SOFTWARE (fc1)
Technical Support: http://www.cisco.com/techsupport

-------------------------
Device ID: R1
Entry address(es):
  IP address: 10.1.1.1
Platform: Cisco 3825,  Capabilities: Router Switch IGMP
Interface: GigabitEthernet1/0/22,  Port ID (outgoing port): GigabitEthernet0/0
Holdtime : 156 sec

Version :
Cisco IOS Software, 3800 Software (C3825-ADVENTERPRISEK9-M), Version 12.4(24)T1, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport

-------------------------
Device ID: R2
Entry address(es):
  IP address: 10.2.2.2
Platform: Cisco 2911,  Capabilities: Router Switch IGMP
Interface: GigabitEthernet1/0/21,  Port ID (outgoing port): GigabitEthernet0/0
Holdtime : 156 sec

Version :
Cisco IOS Software, 2900 Software (C3825-ADVENTERPRISEK9-M), Version 15.2(2)T1, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport

Поиск всех совпадений с регулярным выражением:

In [7]: with open('sh_cdp_neighbors_sw1.txt') as f:
   ...:     sh_cdp = f.read()
   ...:

In [8]: regex = r'Device ID: (\S+).+Platform: \w+ (\S+),.+Cisco IOS Software.+ Version (\S+),'

In [9]: match = re.finditer(regex, sh_cdp, re.DOTALL)

In [10]: for m in match:
    ...:     print(m.groups())
    ...:
('SW2', '2911', '15.2(2)T1')

На первый взгляд, кажется, что вместо трех устройств, в вывод попало только одно. Однако, если присмотреться к результатам, окажется, что кортеже находится Device ID от первого соседа, а платформа и IOS от последнего.

Короткий вариант вывода, чтобы легче было ориентироваться в результатах:

Device ID        Local Intrfce     Holdtme    Capability  Platform  Port ID
SW2              Gi 1/0/16         171              R S   C2960     Gi 0/1
R1               Gi 1/0/22         158              R     C3825     Gi 0/0
R2               Gi 1/0/21         177              R     C2911     Gi 0/0

Так получилось из-за того, что между нужными частями вывода, указана комбинация .+. Без флага re.DOTALL такое выражение захватило бы все до перевода строки, но с флагом оно захватывает максимально длинный кусок текста, так как + жадный. В итоге регулярное выражение описывает строку от первого Device ID до последнего места где встречается Cisco IOS Software.+ Version.

Такая ситуация возникает очень часто при использовании re.DOTALL и чтобы исправить ее, надо не забыть отключить жадность:

In [10]: regex = r'Device ID: (\S+).+?Platform: \w+ (\S+),.+?Cisco IOS Software.+? Version (\S+),'

In [11]: match = re.finditer(regex, sh_cdp, re.DOTALL)

In [12]: for m in match:
    ...:     print(m.groups())
    ...:
('SW2', 'WS-C2960-8TC-L', '12.2(55)SE9')
('R1', '3825', '12.4(24)T1')
('R2', '2911', '15.2(2)T1')