Search code examples
bashawkreplacehexdumpxxd

Replace hex char by a different random one (awk possible?)


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.


Solution

  • 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}"