2

I've got a file consisting of IPs and MAC address pairs and I need to pad the MAC addresses with zeros in each octet, but I don't want to change the IP. So this...

10.5.96.41 0:0:e:4c:b7:42
10.5.96.42 c4:f7:0:13:ef:32
10.5.96.43 0:e8:4c:60:2b:42
10.5.96.44 0:6a:bf:b:35:f1

Should get changed to this...

10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1

I tried sed 's/\b\(\w\)\b/0\1/g' but that produces:

10.05.96.41 00:00:0e:4c:b7:42
10.05.96.42 c4:f7:00:13:ef:32
10.05.96.43 00:e8:4c:60:2b:42
10.05.96.44 00:6a:bf:0b:35:f1

which is not desired because I only want to effect the MAC address portion.

6 Answers6

4

Since you've tagged macos, I'm not sure if this will work for you. I tested it on GNU awk

$ awk '{gsub(/\<[0-9a-f]\>/, "0&", $2)} 1' ip.txt
10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1

awk is good for field processing, here you can simply perform substitution only for second field

But, I see \b and \w with your sed command, so you are using GNU sed? If so,

sed -E ':a s/( .*)(\b\w\b)/\10\2/; ta' ip.txt


With perl

$ perl -lane '$F[1] =~ s/\b\w\b/0$&/g; print join " ", @F' ip.txt
10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1

If you want to get adventurous, specify that you want to avoid replacing first field:

perl -pe 's/^\H+(*SKIP)(*F)|\b\w\b/0$&/g' ip.txt
Sundeep
  • 23,246
  • 2
  • 28
  • 103
  • 1
    The awk version failed on mawk and awk version 20121220, which to my understanding is from the same branch as Mac awk. It worked on gawk and Busybox awk. – James Brown Apr 21 '20 at 15:39
  • 1
    yeah, thought so, but word boundaries is so essential for regex work... – Sundeep Apr 21 '20 at 15:40
  • 1
    This is actually running on Ubuntu, and I'm not sure how that macos tag ended up there. I've removed it. I tried using gsub with awk for a while, but couldn't get the syntax quite right. Your's works perfectly! – user1783253 Apr 21 '20 at 16:15
  • 2
    I'd bet the `macos` tag appeared because you were trying to find a tag for `mac address` and clicked `macos` by mistake. That one tag mistake did color many of the answers you got. – Ed Morton Apr 21 '20 at 16:20
2

With any sed that uses -E to support EREs, e.g. GNU sed or OSX/BSD (MacOS) sed:

$ sed -E 's/[ :]/&0/g; s/0([^:]{2}(:|$))/\1/g' file
10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1

and with any sed:

$ sed 's/[ :]/&0/g; s/0\([^:][^:]:\)/\1/g; s/0\([^:][^:]$\)/\1/' file
10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
2

This might work for you (GNU sed):

sed 's/\b.\(:\|$\)/0&/g' file

Prepend a 0 before any single character followed by a : or the end of line.

Other seds may use:

sed 's/\<.\(:\|$\)/0&/g' file
potong
  • 55,640
  • 6
  • 51
  • 83
0

With GNU sed:

sed -E ':a;s/([ :])(.)(:|$)/\10\2\3/g;ta' file

with any sed:

sed ':a;s/\([ :]\)\(.\):/\10\2:/g;ta' file

Explanation (of the GNU version)

:a  # a label called 'a', used as a jump target
;   # command separator
s   # substitute command ...
/([ :])(.)(:|$)/ # search for any single char which is enclosed by
                 # either two colons, a whitespace and a colon or
                 # a colon and the end of the line ($)
                 # Content between () will be matched in a group
                 # which is used in the replacement pattern

\10\2\3          # replacement pattern: group1 \1, a zero, group2 and 
                 # group3 (see above)
/g               # replace as often as possible
;                # command separator
ta               # jump back to a if the previous s command replaced
                 # something (see below)

The loop using the label a and the ta command is needed because sed won't match a pattern again if input was already part of a replacement. This would happen in this case for example (first line):

0:0

When the above pattern is applied, sed would replace

<space>0: by <space>00: <- colon

The same colon would not match again as the beginning : of the second zero. Therefore the loop until everything is replaced.

hek2mgl
  • 152,036
  • 28
  • 249
  • 266
0

A succinct and precise solution for GNU sed:

sed -Ee 's/\b[0-9a-f](:|$)/0&/gi' file

(On macOS, I recommend installing gsed using brew install gnu-sed.)

0

very circuitous and verbose solution to deal with mawk not having regex :: back-references - the approach is to prepad every slot with extra zeros, then trim out the excess :

nawk ' sub(".+","\5:&:\3", $NF)^_   + gsub(":", "&00") +  \
      gsub("[0-9A-Fa-f]{2}:","\6&") + gsub("[^:]*\6|\5:|:00\3$",_)'
mawk ' sub("^", "\5:", $NF)^_ + gsub(":", "&00") + \
      gsub("[^ :][^ :](:|$)", "\6&") + gsub("[^:]*\6|\5:",_)'
10.5.96.41 00:00:0e:4c:b7:42
10.5.96.42 c4:f7:00:13:ef:32
10.5.96.43 00:e8:4c:60:2b:42
10.5.96.44 00:6a:bf:0b:35:f1

To do it the proper gawk gensub() way -- needed 2 calls to gensub() - calling once ended up missing a few ::

gawk -be 'BEGIN { ___ *= __ = "([ :\t])([[:xdigit:]]?)(:|$)"

_="\\10\\2\\3" } $___ = gensub(__, _, "g", gensub(__, _, "g"))'  
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11