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.


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, arista, exos


$ 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 012345-6789ab 0123456789ab 0x0123456789ab 01 23 45 67 89 AB 012345:6789ab 0123456789ABh
$ echo 01-23-45-67-89-AB | macfmt -v format=linux
$ echo 0123:4567:89AB | macfmt -v format=cisco
$ echo 0123.4567.89ab | macfmt -v format=linux -v cap=upper


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 is the default AWK interpreter there. To fix this, install gawk on Ubuntu and change the first line of the script to:

#! /usr/bin/gawk -f

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:

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)}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 "-" $2 "-" $3 "-" $4 "-" $5 "-" $6)}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6)}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 ":" $3 $4 ":" $5 $6)}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 $2 "." $3 $4 "." $5 $6)}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 "." $2 "." $3 "." $4 "." $5 "." $6)}'
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")}'
gawk 'BEGIN {FPAT="[[:xdigit:]]{2}"};{if(NF==6)print toupper($1 " " $2 " " $3 " " $4 " " $5 " " $6)}'
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.

tr -cd '[:xdigit:][:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1.\2.\3/g'
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'
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'
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'
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1:\2:\3/g'
tr -cd '[:xdigit:][:space:]' | tr '[:lower:]' '[:upper:]' | sed 's/\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)\([[:xdigit:]]\{4\}\)/\1.\2.\3/g'
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'
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'

Python Script

I have written a Python script called, included in the Single File Tools collection, that accepts MAC addresses as command line arguments as an alternative to reading them from STDIN.

back to my homepage.