Languages

Parsing Network Device Output with Regular Expressions in Python

JGJaya Gupta21 Mar 2023 Β· Updated 04 Oct 2026 Β· 8 min read
Parsing Network Device Output with Regular Expressions in Python

Quick answer: Network devices return plain text, not data. Python’s built-in re module lets you describe the shape of the text you want β€” an interface name followed by “is up” or “is down”, an IP address, a MAC address β€” and pull exactly those pieces out of a show command’s output as strings, tuples or dictionaries. Named groups, re.MULTILINE and re.finditer() are the three features that turn a fragile one-liner into a reliable parser.

Once you can connect to a device with Netmiko, parsing becomes the real work: a script that prints show interfaces is only slightly more useful than logging in by hand. This guide teaches the regular expression features that matter for network output, walks through parsing three real commands, compares regex with TextFSM and Genie, and lists the mistakes that make parsers break on the next IOS upgrade.

Why parsing is the hard part of network automation

Most routers and switches were designed for humans reading a terminal. Output is formatted into columns, wrapped at 80 characters and sprinkled with headers, and the exact layout differs between vendors, platforms and even software versions. Until every device speaks NETCONF or RESTCONF with structured YANG data, you will need to extract values from text.

You have three tools for the job:

  • String methods (split(), startswith()) – fine for a single, predictable line.
  • Regular expressions – flexible pattern matching for anything from one value to a whole table. Built into Python, no installation, works on any output.
  • Template parsers (TextFSM, TTP, Cisco Genie) – pre-written parsers for common commands that return dictionaries. Faster to use when a template exists; useless when it does not.

Regex is the skill that makes you independent of templates, and it is what template authors use internally anyway.

The regex features network engineers actually need

Pattern Meaning Network example
\S+ One or more non-whitespace characters Interface name: GigabitEthernet0/1
\d+ One or more digits VLAN ID, packet counters
\d{1,3}(?:\.\d{1,3}){3} Four dot-separated 1–3 digit groups IPv4 address
[0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4} Cisco-style MAC 0050.56be.1a2b
(up|down|administratively down) Alternation Interface status
(?P<name>...) Named capture group Returns a dictionary instead of a tuple
^ and $ with re.MULTILINE Start and end of each line Match one row of a table at a time
\s+ Any amount of whitespace Tolerates column alignment changes

Always write patterns as raw strings (r"...") so backslashes are passed to the regex engine untouched, and compile patterns you reuse in a loop with re.compile().

Hands-on: parsing show interfaces

The classic example. Each interface block in Cisco IOS begins with a line like GigabitEthernet0/1 is up, line protocol is up. We will connect with Netmiko, then extract name, admin status and line-protocol status using named groups so the result is a list of dictionaries, not anonymous tuples.

import os
import re
from netmiko import ConnectHandler

router = {
    "device_type": "cisco_ios",
    "host": "192.168.1.1",
    "username": os.environ["NET_USER"],
    "password": os.environ["NET_PASS"],
}

INTERFACE_RE = re.compile(
    r"^(?P<name>\S+) is (?P<status>up|down|administratively down),"
    r"\s+line protocol is (?P<protocol>up|down)",
    re.MULTILINE,
)

with ConnectHandler(**router) as conn:
    output = conn.send_command("show interfaces")

interfaces = [m.groupdict() for m in INTERFACE_RE.finditer(output)]

for intf in interfaces:
    flag = "" if intf["status"] == "up" and intf["protocol"] == "up" else "  <-- check"
    print(f"{intf['name']:24} {intf['status']:22} {intf['protocol']}{flag}")

Three details make this robust. re.MULTILINE lets ^ anchor to the start of every line, so the pattern cannot accidentally match in the middle of a description. \s+ instead of a single space tolerates extra whitespace. And finditer() with groupdict() gives readable keys, so a colleague reading intf["protocol"] does not have to remember what intf[2] meant.

Hands-on: extracting counters and IP addresses

Values buried inside a block need a two-step approach: split the output into per-interface chunks, then search each chunk. re.split() with a lookahead keeps the interface header attached to its block.

import re

# `output` is the text from `show interfaces` in the previous example
blocks = re.split(r"\n(?=\S+ is (?:up|down|administratively down),)", output)

ERRORS_RE = re.compile(r"(?P<errors>\d+) input errors,\s+(?P<crc>\d+) CRC")
IP_RE     = re.compile(r"Internet address is (?P<ip>\d{1,3}(?:\.\d{1,3}){3})/(?P<prefix>\d+)")

report = {}
for block in blocks:
    name = block.split(" is ", 1)[0].strip()
    errors = ERRORS_RE.search(block)
    ip = IP_RE.search(block)
    report[name] = {
        "ip": f"{ip['ip']}/{ip['prefix']}" if ip else None,
        "input_errors": int(errors["errors"]) if errors else 0,
        "crc": int(errors["crc"]) if errors else 0,
    }

for name, data in report.items():
    if data["crc"] > 0:
        print(f"{name}: {data['crc']} CRC errors on {data['ip']}")

Converting counters with int() at parse time means the rest of your script can compare numbers instead of strings β€” "9" > "10" is True in Python string comparison, a bug that has bitten many first parsers.

Hands-on: a MAC address table into a lookup dictionary

Tables are the most common shape of output. Match one row per line and build whatever structure your task needs β€” here, a MAC-to-port map for a “where is this device plugged in?” tool.

import re

mac_table = """
Vlan    Mac Address       Type        Ports
----    -----------       --------    -----
  10    0050.56be.1a2b    DYNAMIC     Gi1/0/5
  10    0050.56be.3c4d    DYNAMIC     Gi1/0/7
  20    001a.2b3c.4d5e    STATIC      Gi1/0/24
"""

ROW_RE = re.compile(
    r"^\s*(?P<vlan>\d+)\s+(?P<mac>[0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4})"
    r"\s+(?P<type>\w+)\s+(?P<port>\S+)\s*$",
    re.MULTILINE | re.IGNORECASE,
)

mac_to_port = {m["mac"].lower(): (int(m["vlan"]), m["port"]) for m in ROW_RE.finditer(mac_table)}

print(mac_to_port.get("0050.56be.3c4d"))   # (10, 'Gi1/0/7')

Testing against a saved text sample like this β€” no live device needed β€” is the fastest way to develop a pattern. Save real output from your lab into a samples/ folder and write small pytest functions against it.

Regex versus TextFSM and Genie

Netmiko’s send_command(cmd, use_textfsm=True) returns structured data for hundreds of common commands using the ntc-templates library, and Cisco’s Genie parsers cover IOS-XE, NX-OS and IOS-XR in depth. Use them when a template exists β€” there is no point rewriting a parser that thousands of engineers already test. Reach for regex when:

  • No template exists for your command or vendor.
  • You only need one value and a full template is overkill.
  • You are parsing log messages, syslog lines or configuration files rather than show output.
  • You want to write your own TextFSM or TTP template β€” both are built from regular expressions.

Combining the two is common: the broader automation workflow in Why Use Python for Network Engineering? uses TextFSM for inventory and regex for the odd vendor-specific line.

Six parsing mistakes that break on the next upgrade

  1. Matching exact spaces. "is up, line protocol is up" with single spaces fails when a platform pads differently. Use \s+.
  2. Greedy wildcards. .* will happily swallow the rest of the line and the value you wanted with it. Prefer \S+, \d+ or the non-greedy .*?.
  3. Forgetting re.MULTILINE. Without it, ^ matches only the very start of the whole output, so a table pattern returns a single row.
  4. Not handling “no match”. re.search() returns None when nothing matches; calling .group() on it raises AttributeError. Always check if match:.
  5. Parsing the --More-- prompt. If paging was not disabled, the output contains pager artefacts. Netmiko disables paging automatically; raw Paramiko scripts must send terminal length 0 first β€” see Using Paramiko and Netmiko for SSH and Telnet Connections.
  6. Testing only on live devices. Save sample output and write unit tests. A parser that works on one IOS 15 switch may fail on IOS-XE 17; samples from each platform catch that before production does.

Frequently asked questions

Should I use re.match(), re.search() or re.findall()?

match() anchors to the start of the string; search() finds the first occurrence anywhere; findall() returns every occurrence as strings or tuples. For tables, finditer() is best because it yields match objects with groupdict().

How do I test a regex quickly?

Paste sample output into regex101.com with the Python flavour selected, or use the Python REPL. Keep the samples that work as test fixtures.

Can regex parse JSON or XML output?

Do not. If a device supports | json (NX-OS, EOS, Junos) or NETCONF, use json.loads() or an XML library. Regex is for text that has no structured alternative.

Is regex slow for large outputs?

Not in practice. Compiled patterns process a show interfaces dump from a 48-port switch in milliseconds. Avoid patterns with nested quantifiers like (\S+\s*)+, which can backtrack catastrophically.

Key takeaways

  • Regex turns human-formatted show output into Python data without any extra libraries.
  • Named groups, re.MULTILINE and finditer() are the three features that make parsers readable and reliable.
  • Use TextFSM or Genie when a template exists; write regex when it does not or when you need one value.
  • Tolerate whitespace, avoid greedy wildcards, check for None, and test against saved samples.

Want to go deeper into Python string handling, data structures and the analysis skills that turn parsed output into dashboards? Our Python & Data Science course covers Python from fundamentals to real projects, with mentor support and placement assistance. For more tutorials, subscribe to our YouTube channel.

JG
Written byJaya Gupta

Part of the Techknowledgehub team of industry mentors, writing practical guides to help you build a job-ready tech career.

More articles by Jaya Gupta β†’
Keep reading

Related articles

Leave a Reply