patternMatches
patternMatches(config: List<ConfigLine>, pattern: Pattern<T>) : Bag<{line: ConfigLine, data: T}>
Matches pattern against the text of each config line in config and returns one record for each line that matches.
Lines that do not match are omitted. If no line matches, the result is an empty bag.
Each record has the following fields:
line: ConfigLineThe matching config line.data: TThe data extracted by the pattern, as returned bypatternMatch.
The result is a Bag, so the records have no defined order.
See Details on Patterns for the full pattern syntax.
Matching rules
- Each line is matched with the same rules as
patternMatch. See patternMatch matching rules. - Only the lines in
configare matched. The children of those lines are not searched. Passmatch.line.childrento a secondpatternMatchescall to match nested lines (see Matching nested lines). nullitems inconfigare skipped.
Null inputs
If config or pattern is null, the query fails with the error
First argument to “patternMatches” was null; consider adding an isPresent() condition. (or Second argument for
pattern). The config field of a device's files is nullable. Use
isPresent to exclude null values before calling patternMatches.
Examples
The first column lists the text of each line in config, in order.
The text of each line in config | pattern | The data of each match | Remarks |
|---|---|---|---|
"interface Ethernet1/1", "interface Ethernet1/2" | `interface {name:string}` | {name: "Ethernet1/1"}, {name: "Ethernet1/2"} | Each matching line produces one record in the result. |
"hostname r1", "ntp server 10.0.0.1", "ntp server 10.0.0.2" | `ntp server {server:ipv4Address}` | {server: 10.0.0.1}, {server: 10.0.0.2} | Lines that do not match, such as hostname r1, are omitted from the result. |
"hostname r1", "feature bgp" | `ntp server {server:ipv4Address}` | No matches | If no line matches, the result is an empty bag. |
"feature bgp", "feature ospf" | `feature bgp` | {} | A pattern with no components extracts no data. Use it to find lines with a given form; the line field of each record holds the matching line. |
"shutdown", "no shutdown" | `shutdown` | {} | The pattern must match at the start of each line. no shutdown does not start with the literal shutdown, so only the first line matches. |
"logging host 10.0.0.1", "logging host 10.0.0.1 vrf mgmt" | `logging host {host:ipv4Address} {eof}` | {host: 10.0.0.1} | {eof} requires the pattern to match the whole line. The second line has the text vrf mgmt after the address, so it does not match. |
You can try out any of these examples by copying the lines and pattern above into the following query template:
lines = [...];
pattern = ...;
config = parseConfigBlocks(OS.IOS, join("\n", lines));
foreach match in patternMatches(config, pattern)
select { data: match.data }
Example query
The following query lists the TACACS servers configured on each device, along with the text of the matching config line.
foreach device in network.devices
where isPresent(device.files.config)
foreach match in patternMatches(device.files.config, `tacacs-server host {server:string}`)
select {device: device.name, tacacsServer: match.data.server, line: match.line.text}
patternMatches returns a bag, so the query uses a foreach qualifier to iterate over the matches. Lines that do not
match the pattern are not in the bag, so the query needs no isPresent check on each match, unlike a query that uses
patternMatch.
Matching nested lines
Use the children field of a matching line to match the lines nested under it. The following query lists the speed of
each interface on Cisco NX-OS devices. The first patternMatches call matches the top-level interface lines, and the
second call matches the speed lines nested under each interface.
foreach device in network.devices
where device.platform.vendor == Vendor.CISCO && device.platform.os == OS.NXOS
&& isPresent(device.files.config)
foreach ifaceMatch in patternMatches(device.files.config, `interface {iface:string}`)
foreach speedMatch in patternMatches(ifaceMatch.line.children, `speed {speed:number}`)
select {device: device.name, iface: ifaceMatch.data.iface, speed: speedMatch.data.speed}
Calling patternMatches(device.files.config, `speed {speed:number}`) directly returns no matches for this
configuration, because the speed lines are children of the interface lines, not top-level lines.
patternMatches also accepts a Bag<ConfigLine> as its first argument. A Bag has no defined order, so this overload
produces a warning. Use orderBy() or order() to convert the argument to a List<ConfigLine>. In a future release,
the warning will become a type-checking error.
The blockMatches builtin provides an alternative method for
matching multiple, nested lines of configuration, and is often easier to use than patternMatches. For instance, the
example above can be expressed more succinctly using blockMatches as follows:
blockPattern = ```
interface {iface:string}
speed {speed:number}
```;
foreach device in network.devices
where device.platform.vendor == Vendor.CISCO && device.platform.os == OS.NXOS
&& isPresent(device.files.config)
foreach match in blockMatches(device.files.config, blockPattern)
select { device: device.name, iface: match.data.iface, speed: match.data.speed }
Prefer using blockMatches to match nested lines, unless you need more control over the matching logic than is provided
by block patterns.
See also
Guides
- Extracting Data from Configurations via Pattern Matching
- Collections
- Block patterns Use block patterns for most nested, multline patterns.
- Regular Expressions Use regular expressions when you want more control over fine-details of white-space and other aspects of the patterns. Regular expressions are typically more appropriate when extracting information from non-configuration files.
Types
Functions
- patternMatch: Matches a pattern against a single string, and
returns the extracted data or
null. - blockMatches: Matches a block pattern against nested config lines.
- parseConfigBlocks: Parses a string that holds a device configuration into a list of config lines.