Regex returning undesired value

224 Views Asked by At

I am trying to create a script that will comb through a Cisco "show run all" file and return an interface based on if a command appears. I have a start of Regex, but it's matching on the previous interface and not the desired interface.

My regex is interface (\S+)(?s).*?access-session port-control force-authorized

Sample information below. The desired result would be GigabitEthernet1/0/45, but instead it is returning GigabitEthernet1/0/44.

Sample Data:

interface GigabitEthernet1/0/44  
access-session port-control auto 
!
interface GigabitEthernet1/0/45  
access-session port-control force-authorized 
3

There are 3 best solutions below

3
On BEST ANSWER

Alright, so as I thought I was close, but here is what works. Basically I had to tell it to stop matching at the ! which separates the interface config within the config file. (adding [^!] )

interface (\S+)(?s)[^!].*?access-session port-control force-authorized
0
On

Try the following match pattern.

(?<=interface ).+?(?=\s+access-session port-control force-authorized)

Or, capture pattern.

interface (.+?)\s+access-session port-control force-authorized
0
On

The following regex will do (replace (?s).*? with \s+):

interface (\S+)\s+access-session port-control force-authorized
  • For an explanation and the option to experiment with this regex, see this regex101.com page.

  • You don't need inline option (?s) (which makes . match newlines too), given that \s matches a newline char. too and given that all you need to match is whitespace.

  • By not matching a run of any characters via .*?, there is no need to then exclude characters, as in your own solution attempt.

    • Here's a fixed version of your own attempt - but note that there's no need for this complexity (unless your sample data isn't truly representative of your real data):

      interface (\S+)[^!]+?access-session port-control force-authorized
      
      • [^!]+? non-greedily matches any nonempty run of characters (including newlines) not containing !, and thereby prevents matching across the ! char. in the input.

As for what you tried:

  • interface (\S+) starts matching at the first interface block, and .*? then successfully matches any text across the subsequent interface block until the access-session port-control force-authorized text is found. That is, you therefore capture the first interface block's name, not that of the one the text of interest is associated with.

  • In other words: using the non-greedy form of .*, .*?, does not protect you from matching the preceding subexpression (interface (\S+)) again (given that any run of characters is matched) before the (closest, due to the non-greediness) succeeding one (access-session port-control force-authorized) is matched.


A complete PowerShell example (note that the input must be provided as a single, multiline string, given that you want to match across lines):

$str = @'
interface GigabitEthernet1/0/44  
access-session port-control auto 
!
interface GigabitEthernet1/0/45  
access-session port-control force-authorized
'@ 

if ($str -match 'interface (\S+)\s+access-session port-control force-authorized') {
  # Output what the 1st capture group matched.
  $Matches.1
}

The above outputs 'GigabitEthernet1/0/45', as desired.