Skip to content

Instantly share code, notes, and snippets.

@boppreh
Created November 17, 2021 12:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boppreh/091b81ab0e2f879cc6fad2ade92029de to your computer and use it in GitHub Desktop.
Save boppreh/091b81ab0e2f879cc6fad2ade92029de to your computer and use it in GitHub Desktop.
Parse `ipconfig /all` in Python
import subprocess
import collections
Interface = collections.namedtuple('Interface', 'name description subnet_mask ipv4_addresses ipv4_gateway ipv6_addresses ipv6_gateway dhcp_server dns_servers')
def parse_ipconfig():
"""
Parses results from ipconfig. PowerShell has more structured functions, but
they don't serialize properly
(https://stackoverflow.com/questions/69997138/serialization-differences-between-powershells-format-list-and-convertto-json).
A normal output looks like this:
Wireless LAN adapter Local Area Connection* 12:
Autoconfiguration Enabled . . . . : Yes
DHCP Enabled. . . . . . . . . . . : Yes
Physical Address. . . . . . . . . : 9C-DA-3E-68-11-37
Description . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
Connection-specific DNS Suffix . :
Media State . . . . . . . . . . . : Media disconnected
Wireless LAN adapter Local Area Connection* 1:
NetBIOS over Tcpip. . . . . . . . : Enabled
fec0:0:0:ffff::3%1
fec0:0:0:ffff::2%1
DNS Servers . . . . . . . . . . . : fec0:0:0:ffff::1%1
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-24-A6-E0-0A-9C-DA-3E-68-11-36
DHCPv6 IAID . . . . . . . . . . . : 956956711
Default Gateway . . . . . . . . . :
Subnet Mask . . . . . . . . . . . : 255.255.255.0
IPv4 Address. . . . . . . . . . . : 192.168.56.1(Preferred)
Link-local IPv6 Address . . . . . : fe80::d83f:9609:86ff:2b57%23(Preferred)
Autoconfiguration Enabled . . . . : Yes
DHCP Enabled. . . . . . . . . . . : No
Physical Address. . . . . . . . . : 0A-00-27-00-00-17
Description . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter
Connection-specific DNS Suffix . :
"""
result = subprocess.run(["ipconfig", "/all"], capture_output=True, text=True)
assert result.returncode == 0
interfaces = []
lines = result.stdout.strip().split('\n')
while lines:
# Parse an interface section.
interface_name = lines.pop(0)
# Ignore the empty line after the interface name.
assert not lines.pop(0)
# Parse section line by line.
attributes = {'Name': interface_name}
last_attribute_name = None
while lines:
line = lines.pop(0)
# Sections are separated by empty lines.
if not line:
break
# Fields may encode multiple values in separate, indented lines:
#
# Default Gateway . . . . . . . . . : fe80::1%8
# 192.168.2.1
if line.startswith(' '):
# We are in a continuation value, assume the name is the same
# as before.
value = line
attribute_name = last_attribute_name
else:
left, value = line.split(' : ')
attribute_name = left.strip('. ')
# IP addresses are often formatted as "192.168.56.1(Preferred)"
# or "192.168.56.1(Deprecated)". Remove those labels.
value = value.replace('(Preferred)', '').replace('(Deprecated)', '').strip()
if not value:
# Skip empty values. They'll be converted to None's at the end.
continue
if attribute_name == last_attribute_name:
# Some sections may contain duplicated fields, or fields spanning
# multiple lines. Put the values in a list.
if not isinstance(attributes[last_attribute_name], list):
attributes[last_attribute_name] = [attributes[last_attribute_name]]
attributes[last_attribute_name].append(value)
else:
if attribute_name in ('Default Gateway', 'IPv4 Address', 'IPv6 Address', 'DNS Servers'):
# Fields that should always be lists, even if just one value
# is provided.
value = [value]
attributes[attribute_name] = value
last_attribute_name = attribute_name
field_name_mapping = {'name': 'Name', 'description': 'Description', 'subnet_mask': 'Subnet Mask', 'ipv4_addresses': 'IPv4 Address', 'ipv6_addresses': 'IPv6 Address', 'dhcp_server': 'DHCP Server', 'dns_servers': 'DNS Servers'}
# Gateways are given with IPv4 and IPv6 mixed, and we have to fix that.
ipv4_gateway = ''
ipv6_gateway = ''
for gateway in attributes.get('Default Gateway', []):
if ':' in gateway:
ipv6_gateway = gateway
else:
ipv4_gateway = gateway
interface = Interface(ipv4_gateway=ipv4_gateway, ipv6_gateway=ipv6_gateway, **{n: attributes.get(m) for n, m in field_name_mapping.items()})
interfaces.append(interface)
return interfaces
if __name__ == '__main__':
from pprint import pprint
pprint(parse_ipconfig())
# Example interface:
# Interface(name='Ethernet adapter VirtualBox Host-Only Network:', description='VirtualBox Host-Only Ethernet Adapter', subnet_mask='255.255.255.0', ipv4_addresses=['192.168.56.1'], ipv4_gateway='', ipv6_addresses=None, ipv6_gateway='', dhcp_server=None, dns_servers=['fec0:0:0:ffff::1%1', 'fec0:0:0:ffff::2%1', 'fec0:0:0:ffff::3%1'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment