1

have a mac address and I need to replace only one hex char (one at a very specific position) by a different random one (it must be different than the original). I have it done in this way using xxd and it works:

#!/bin/bash
mac="00:00:00:00:00:00" #This is a PoC mac address obviously :)
different_mac_digit=$(xxd -p -u -l 100 < /dev/urandom  | sed "s/${mac:10:1}//g" | head -c 1)
changed_mac=${mac::10}${different_mac_digit}${mac:11:6}
echo "${changed_mac}" #This echo stuff like 00:00:00:0F:00:00

The problem for my script is that using xxd means another dependency... I want to avoid it (not all Linux have it included by default). I have another workaround for this using hexdump command but using it I'm at the same stage... But my script already has a mandatory awk dependency, so... Can this be done using awk? I need an awk master here :) Thanks.

OscarAkaElvis
  • 5,384
  • 4
  • 27
  • 51
  • 1
    Sorry not clear. Can you clarify what are you trying to do? – anubhava Mar 02 '19 at 11:55
  • I'm trying to change one digit of a mac address. I need that specific position and I need to replace it by another random different hex char. And I need to do it using awk if possible inestead of using in the way I already have it done. Execute the poc and you'll see – OscarAkaElvis Mar 02 '19 at 12:02
  • Changing mac address at a specified position is fine but is generating random string by calling `xxd` also a requirement? – anubhava Mar 02 '19 at 12:04
  • A different char is a requirement... but i'm trying to avoid the use of xxd – OscarAkaElvis Mar 02 '19 at 12:14

5 Answers5

2

Something like this may work with seed value from $RANDOM:

mac="00:00:00:00:00:00"
awk -v seed=$RANDOM 'BEGIN{ FS=OFS=":"; srand(seed) } {
   s="0"
   while ((s = sprintf("%x", rand() * 16)) == substr($4, 2, 1))
   $4 = substr($4, 1, 1) s
} 1' <<< "$mac"

00:00:00:03:00:00

Inside while loop we continue until hex digit is not equal to substr($4, 2, 1) which 2nd char of 4th column.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Wait... this is not working if you set mac as "11:11:11:11:11:11", it is not printing the mac with only one char modified... it prints "11:11:11:03:11:11" but it should be "11:11:11:13:11:11" – OscarAkaElvis Mar 02 '19 at 13:23
1

bash has printf builtin and a random function (if you trust it):

different_mac_digit() {
    new=$1
    while [[ $new = $1 ]]; do
        new=$( printf "%X" $(( RANDOM%16 )) )
    done
    echo $new
}

Invoke with the character to be replaced as argument.

jhnc
  • 11,310
  • 1
  • 9
  • 26
1

You don't need xxd or hexdump. urandom will also generate nubmers that match the encodings of the digits and letters used to represent hexadecimal numbers, therefore you can just use

old="${mac:10:1}"
different_mac_digit=$(tr -dc 0-9A-F < /dev/urandom | tr -d "$old" | head -c1)

Of course, you can replace your whole script with an awk script too. The following GNU awk script will replace the 11th symbol of each line with a random hexadecimal symbol different from the old one. With <<< macaddress we can feed macaddress to its stdin without having to use echo or something like that.

awk 'BEGIN { srand(); pos=11 } {
  old=strtonum("0x" substr($0,pos,1))
  new=(old + 1 + int(rand()*15)) % 16
  print substr($0,1,pos-1) sprintf("%X",new) substr($0,pos+1)
}' <<< 00:00:00:00:00:00

The trick here is to add a random number between 1 and 15 (both inclusive) to the digit to be modified. If we end up with a number greater than 15 we wrap around using the modulo operator % (16 becomes 0, 17 becomes 1, and so on). That way the resulting digit is guaranteed to be different from the old one.

However, the same approach would be shorter if written completely in bash.

mac="00:00:00:00:00:00"
old="${mac:10:1}"
(( new=(16#"$old" + 1 + RANDOM % 15) % 16 ))
printf %s%X%s\\n "${mac::10}" "$new" "${mac:11}"

"One-liner" version:

mac=00:00:00:00:00:00
printf %s%X%s\\n "${mac::10}" "$(((16#${mac:10:1}+1+RANDOM%15)%16))" "${mac:11}"
Socowi
  • 25,550
  • 3
  • 32
  • 54
1

Another awk:

$ awk -v n=11 -v s=$RANDOM '                # set n to char # you want to replace
BEGIN { FS=OFS="" }{                        # each char is a field
    srand(s)                                
    while((r=sprintf("%x",rand()*16))==$n);  
    $n=r
}1' <<< $mac

Output:

00:00:00:07:00:00

or oneliner:

$ awk -v n=11 -v s=$RANDOM 'BEGIN{FS=OFS=""}{srand(s);while((r=sprintf("%x",rand()*16))==$n);$n=r}1' <<< $mac
James Brown
  • 36,089
  • 7
  • 43
  • 59
1
$ mac="00:00:00:00:00:00"
$ awk -v m="$mac" -v p=11 'BEGIN{srand(); printf "%s%X%s\n", substr(m,1,p-1), int(rand()*15-1), substr(m,p+1)}'
00:00:00:01:00:00
$ awk -v m="$mac" -v p=11 'BEGIN{srand(); printf "%s%X%s\n", substr(m,1,p-1), int(rand()*15-1), substr(m,p+1)}'
00:00:00:0D:00:00

And to ensure you get a different digit than you started with:

$ awk -v mac="$mac" -v pos=11 'BEGIN {
    srand()
    new = old = toupper(substr(mac,pos,1))
    while (new==old) {
        new = sprintf("%X", int(rand()*15-1))
    }
    print substr(mac,1,pos-1) new substr(mac,pos+1)
}'
00:00:00:0D:00:00
Ed Morton
  • 188,023
  • 17
  • 78
  • 185