0

I am looking for a single regexp that will match either of these 2 lines, and capture/group the values for speed and duplex.

speed: 1000Mbps (Duplex: full)
speed: n/a

Line #1 should have group 1 with value '1000Mbps' and group 2 value 'full'. Line #2 would have group 1 only, with value 'n/a'

This is closest I could get, trying to use an alternate inside a nested group. It gets me speed values, for not duplex values.

^\s+speed:\s+(\S+)\s*(|\(Duplex:\s+(\S+)\))

Longer Version

This is for a TextFSM template. I will include those details here, in case using FSM there is an easy way to accomplish the above.

Sample data:

== [onboard]
        ==[mgmt]
                mode: static
                ip: 1.2.3.4 255.255.255.248
                ipv6: ::/0
                status: up
                speed: 1000Mbps (Duplex: full)
        ==[port1]
                mode: static
                ip: 0.0.0.0 0.0.0.0
                ipv6: ::/0
                status: down
                speed: n/a

Here is the template file I am using:

Value Required NAME (\S+)
Value MODE (\S+)
Value IP (\d+?\.\d+?\.\d+?\.\d+?)
Value NETMASK (\S+)
Value STATUS (\S+)
Value SPEED (\S+)
Value DUPLEX (\S+)

Start
  ^==\s+\[onboard\]
  ^\s+==\[${NAME}\]
  ^\s+mode:\s+${MODE}
  ^\s+ip:\s+${IP}\s+${NETMASK}
  ^\s+ipv6:\s+.*
  ^\s+status:\s+${STATUS}
  ^\s+speed:\s+${SPEED}\s+\(Duplex:\s+${DUPLEX}\) -> Record
  ^\s*$$
  ^. -> Error

I've made several attempts to the above, including a 2nd line to match 'speed' that doesn't have 'Duplex' on it. Tried having both lines Record action, or just one, or the last catch-all. Is there someway to specify an OR type of statement in FSM? Attempt to match input line against this, if no match, try against that?

Edit: thanks for the replies. I am not able to get any of those examples working in TextFSM however.

refriedjello
  • 657
  • 8
  • 18
  • 1
    You need to put the first bit in its own capture group, remove the pipe from the second group, and add a `?` qualifier after the second capture group. This will ensure that you're always looking for that first group as well as 0 or 1 occurrences of the second group. Using two capture groups will allow you to retain both when doing a `.findall()` Using the `|` for 'or' I don't think is necessary here. – whege Jan 07 '21 at 20:30
  • 1
    To get all those value in the same order, you could use `^\s*==\[([^][\s]+)]\r?\n\s*mode:\s*(\S+)\r?\n\s*ip:\s*(.+)\r?\n\s*ipv6:\s*(.+)\r?\n\s*status:\s*(\S+)\r?\n\s*speed:\s*(\S+)(?:\s*(\([^()]+\)))?` https://regex101.com/r/J2DQ1H/1 – The fourth bird Jan 07 '21 at 21:53

2 Answers2

1

A regex that could capture these values is:

(?:speed:\s)(\d+Mbps|n\/a)(?:(?:\s\(Duplex:\ (\w+)\))|$)

Demo: https://regex101.com/r/DYhDXc/1/

alex-dl
  • 802
  • 1
  • 5
  • 12
1

A couple of things for this:

  1. The point of TextFSM is to simplify using regex by trying to find a match using several regex statements until a match is found. So there isn't a need to have 1 regex line per 1 desired capture. You could simply do this:
  ^\s+speed:\s+${SPEED}\s+\(Duplex:\s+${DUPLEX}\) -> Record
  ^\s+speed:\s+${SPEED} -> Record

If you just want to know how regex can be used for a single regex statement, then what others have said will apply, although it might not be as clear since it is strictly in terms of regex and not in relation to TextFSM. The ^\s+speed:\s+${SPEED}\s+\(Duplex:\s+${DUPLEX}\) needs to allow for, but not require everything after ${SPEED}. In regex, you can do this by wrapping this section in a group, and adding a ? after the group as @LiamFiddler mentioned. That would just add (...)?, looking like ^\s+speed:\s+${SPEED}(\s+\(Duplex:\s+${DUPLEX}\))?

  1. You are putting your Record at the end of text that is non-tabular. This is not reliable as the output could vary between HW and SW (even if all current combinations work, there is nothing guaranteeing it will be so in the next release). If the speed were suddenly moved to the first field, then you would be off by one on all of the other fields. It is best to match on the first line and use Continue.Record
Start
  ^==\s+\[onboard\]
  ^\s+==\[\S+\] -> Continue.Record
  ^\s+==\[${NAME}\]

This will Record entries at when a new interface is found, but before it captures the ${NAME} field for the following entry. The line with the interface name will continue to be used for additional matches since Continue is provided, and it will start looking for matches after the current regex statement ( ^\s+==\[${NAME}\]). The First Record will not have a record since it is empty, and the last entry will be recorded since TextFSM defaults to having an implicit final Record.

Jacob
  • 66
  • 4
  • Thank you for the detailed and illuminating answer. Unfortunately the code example for TextFSM is not working for me. I was able to get these two lines working: `^\s+speed:\s+${SPEED}(|Mbps)\s+\(Duplex:\s+${DUPLEX}\)$$ -> Record` and `^\s+speed:\s+${SPEED}$$ -> Record` – refriedjello Jan 09 '21 at 03:01