Converting MAC Address Formats with macfmt

MAC addresses are written in an astonishingly big number of slightly different ways. As long as they are intended for human consumption, that is OK. But if they are used to match on program output, e.g to check the ARP table of a router, the correct format for that router model has to be used. Manual conversion is easy, but nevertheless error-prone. Thus I have written a small AWK script called macfmt to convert from and to the MAC address formats I know. It is included in my Single File Tools collection.

Usage

macfmt [-v cap=CASE] [-v format=FORMAT]
  CASE   is one of: lower, upper
  FORMAT is one of: all, cisco, linux, enterasys, cabletron, broadcom, vmps, netsight, hp, hex, hexprefix, pxe, pgsql, hexpostfix, exos, arista, mikrotik, huawei, ieee, ietf, dell, ftos, dellos9, macos, apple_ios, android, chromeos, ps3, ps4, ps5, switch, wii, xbox, xbox360, windows, windows_arp

Examples

$ echo 01:23:45:67:89:ab | macfmt
0123.4567.89ab 01:23:45:67:89:ab 01-23-45-67-89-AB 01:23:45:67:89:AB 0123:4567:89AB 0123.4567.89AB 01.23.45.67.89.AB 012345-6789ab 0123456789ab 0x0123456789ab 01 23 45 67 89 AB 012345:6789ab 0123456789ABh 0123-4567-89ab 01-23-45-67-89-ab 0123456789AB
$ echo 01-23-45-67-89-AB | macfmt -v format=linux
01:23:45:67:89:ab
$ echo 0123:4567:89AB | macfmt -v format=cisco
0123.4567.89ab
$ echo 0123.4567.89ab | macfmt -v format=linux -v cap=upper
01:23:45:67:89:AB

AWK Compatibility

I have developed macfmt on Solaris 10 using its new AWK (/usr/xpg4/bin/awk). It works as well with Gawk, the GNU Project's AWK implementation. I have intended macfmt to work with any POSIX compatible AWK, help in improving compatibility is welcome.

macfmt does not work with mawk versions below 1.3.4, because those did not support character classes in regular expressions. This might be a problem for some Ubuntu users, because mawk was the default AWK interpreter there for a long time. To fix this, install gawk on Ubuntu and change the first line of the script to:

#! /usr/bin/gawk -f
Ubuntu 20.04 LTS provides mawk version 1.3.4, thus macfmt works there with either gawk or mawk as /usr/bin/awk. On Debian GNU/Linux and derivatives thereof like Ubuntu GNU/Linux, the Debian Alternatives System (Debian Wiki, Debian Reference) can be used to select which AWK implementation provides /usr/bin/awk.

Alternatives

The macfmt AWK script works for me, but it has an uncommon user interface and weird default ouput. Then not all systems provide a sufficient AWK implementation. Thus I have looked for alternative solutions for the problem space.

Python Script macfmt.py

I have written a Python script called macfmt.py, also included in the Single File Tools collection, that accepts MAC addresses either as command line arguments or on standard input (STDIN). This script prints each input MAC address in only one format, selectable via the --format FMT option.

Gawk One-Liners

Gawk supports defining fields by content using FPAT. This enables one-line Gawk programs for MAC address formatting, one for each of the different output formats, e.g.:

Cisco (or Arista)
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print tolower($1 $2 "." $3 $4 "." $5 $6)}'
Linux (or ExtremeXOS)
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print tolower($1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6)}'
Enterasys
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 "-" $2 "-" $3 "-" $4 "-" $5 "-" $6)}'
Cabletron
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6)}'
Broadcom
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 ":" $3 $4 ":" $5 $6)}'
VMPS
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 "." $3 $4 "." $5 $6)}'
NetSight
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 "." $2 "." $3 "." $4 "." $5 "." $6)}'
HP
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print tolower($1 $2 $3 "-" $4 $5 $6)}'
Hexadecimal without Prefix
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print tolower($1 $2 $3 $4 $5 $6)}'
Hexadecimal with Prefix
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print tolower("0x" $1 $2 $3 $4 $5 $6)}'
Hexadecimal with Postfix
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 $3 $4 $5 $6 "h")}'
PXE
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 " " $2 " " $3 " " $4 " " $5 " " $6)}'
PostgreSQL
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 $3 ":" $4 $5 $6)}'

POSIX Compatible One-Liners Using tr and sed

In the case that only valid MAC addresses separated by whitespace are used as input, POSIX provides tools for one-line command pipelines using tr and sed that fulfill the role of macfmt. Each command pipeline is based on the same tr invocation to remove all but the hexadecimal digits of the MAC address and conserving whitespace, followed by case conversion and digit grouping according to the target format.

Cisco
tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1.\2.\3/g'
Linux
tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)/\1:\2:\3:\4:\5:\6/g'
Enterasys
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)/\1-\2-\3-\4-\5-\6/g'
Cabletron
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)/\1:\2:\3:\4:\5:\6/g'
Broadcom
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1:\2:\3/g'
VMPS
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1.\2.\3/g'
NetSight
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)\([[:xdigit:]]\{2\}\)/\1.\2.\3.\4.\5.\6/g'
HP
tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/\([[:xdigit:]]\{6\}\)\([[:xdigit:]]\{6\}\)/\1-\2/g'
Hexadecimal without Prefix
tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]'
Hexadecimal with Prefix
tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/\([[:xdigit:]]\{12\}\)/0x\1/g'


back to my homepage.