20

How can i generate a valid random mac adress with bash.

The first half of the adress should always stay same like this

00-60-2F-xx-xx-xx

just the x value should be generated random?

NES
  • 201

15 Answers15

25

In the past I've done this using:

echo 00-60-2F-$[RANDOM%10]$[RANDOM%10]-$[RANDOM%10]$[RANDOM%10]-$[RANDOM%10]$[RANDOM%10]

but that will only make them in the range 0-9. For my purposes, that was good enough.

Probably a better solution would be to use printf:

printf '00-60-2F-%02X-%02X-%02X\n' $[RANDOM%256] $[RANDOM%256] $[RANDOM%256]

Here's how that works:

  • The printf program is based on the C "printf" function, which takes a "format string" as the first parameter and then additional parameters fill in the format string.
  • % in the format string introduces a "format specifier" which can be one or more characters telling how to format the arguments.
  • A leading zero (0) in a format specifier means that the resulting numeric output should be padded with leading zeros up to the specified width.
  • The 2 says that the specifier should be displayed taking up two characters worth of width.
  • The X ends the specifier and denotes that it should be interpreted as a number and displayed as hexidecimal. Because it's upper-case, the letters a-f should be upper case.
  • The \n is a newline -- printf interprets backslash as an escape code which can be used to display other characters, often tricky characters like the newline.
  • The remaining characters in the format specifier are printed out literally, this includes the initial "00-06-2F-" and the dashes between the format specifiers.
  • The remaining arguments are shell variable substitutions (denoted by the $) and include a math expression which is a random number (RANDOM) modulo 256. This results in a random number between 0 and 255.
19

Here is a fish.

This shell script will generate the random string you seek:

#!/bin/bash
hexchars="0123456789ABCDEF"
end=$( for i in {1..6} ; do echo -n ${hexchars:$(( $RANDOM % 16 )):1} ; done | sed -e 's/\(..\)/-\1/g' )
echo 00-60-2F$end

I did just have something here that showed how to run it from the command line, but after looking at Dennis Williamson convoluted (but upvoted) solution I see that the answer that people expect is the one where they don't have to do any work themselves.

deltaray
  • 2,077
17
  1. Generate an appropriately sized int like so: http://tldp.org/LDP/abs/html/randomvar.html
  2. Convert to hex like so: http://snipplr.com/view/2428/convert-from-int-to-hex/
  3. Add the dashes between three randomly generated chunks
#!/bin/bash
RANGE=255
#set integer ceiling

number=$RANDOM
numbera=$RANDOM
numberb=$RANDOM
#generate random numbers

let "number %= $RANGE"
let "numbera %= $RANGE"
let "numberb %= $RANGE"
#ensure they are less than ceiling

octets='00-60-2F'
#set mac stem

octeta=`echo "obase=16;$number" | bc`
octetb=`echo "obase=16;$numbera" | bc`
octetc=`echo "obase=16;$numberb" | bc`
#use a command line tool to change int to hex(bc is pretty standard)
#they're not really octets.  just sections.

macadd="${octets}-${octeta}-${octetb}-${octetc}"
#concatenate values and add dashes

echo $macadd
#echo result to screen
#note: does not generate a leading zero on single character sections.  easily remediedm but that's an exercise for you

Or in python:

from random import randint
def gen_mac_char():
  return hex((randint(0,16))).split('x')[1]
def gen_mac_pair():
  return ''.join([gen_mac_char(), gen_mac_char()])
def gen_last_half_mac(stem):
  return '-'.join([stem, gen_mac_pair(), gen_mac_pair(), gen_mac_pair()])
print(gen_last_half_mac('00-60-2F'))

Note that the python version only uses a 16 wide field to generate a hex char, so you don't have to worry about zero padding - approach amended to address a comment.

Giacomo1968
  • 58,727
RobotHumans
  • 5,934
15

Using standard tools, either

# output in capitals
hexdump -n3 -e'/3 "00-60-2F" 3/1 "-%02X"' /dev/random

or

# output in lower case letters
echo 00-60-2f$(od -txC -An -N3 /dev/random|tr \  -)

might be the shortest of all.

wnrph
  • 3,715
  • 1
  • 28
  • 39
7
#!/bin/bash
LC_CTYPE=C
MAC=00-60-2F
for i in {1..3}
do
    IFS= read -d '' -r -n 1 char < /dev/urandom
    MAC+=$(printf -- '-%02x\n' "'$char")
done
printf '%s\n' "$MAC"

The keys to the way this works:

  • LC_CTYPE=C - allows characters > 0x7F

  • IFS= - disables interpretation of \t (tab), \n (newline) and space

  • -d ''- allows newlines

  • -r allows \ (and should almost always be used by habit with read)

  • The format specifier -%02x\n causes the output to be a literal hyphen followed by a two-digit hexadecimal number including a leading zero, if appropriate. The newline is superfluous here and could be omitted.

  • The read gets a single byte (-n 1) from /dev/urandom in the range 0 to 255 (00 to FF).

  • The single quote in the last argument to the printf in the loop causes the character to be output as its numeric value ("A" is output as "65"). See the POSIX specification for printf where it says:

    If the leading character is a single-quote or double-quote, the value shall be the numeric value in the underlying codeset of the character following the single-quote or double-quote.

7

The shortest way I could come up with was using hexdump directly

echo 00-60-2f$(hexdump -n3 -e '/1 "-%02X"' /dev/random)
  • -n3 tells hexdump to read three byytes
  • The format string prints a dash and a two digit hex value for each byte
    • '/1' means apply format for every read byte
    • "-%02X" is a printf spec for printing a leading zero, two digit, upper case hex value
  • /dev/random is a source random bytes

Tested on GNU/Linux

4

Another one line solution

$ echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /-/g'

Same thing in upper case

$ echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /-/g' | tr '[:lower:]' '[:upper:]'

Generate it for a Bash environment variable

$ export MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /-/g')
$ echo $MAC

Details:

  • od (octal dump)

    -An Suppresses the leading address representation (extra noise) of the output.
    -N3 Limit the output to three bytes.
    -t xC Output in hex, ASCII character style, as desired.
    /dev/urandom Linux kernel random number pseudo-file.

  • sed (stream editor) For space to hyphen substitution.

    -e <SCRIPT> execute the sed script.

  • tr (string translation) Optional, in this example. I like upper case MAC addresses in my scripts/environment.

zero2cx
  • 641
2

One liner (bash)

mac=$(c=0;until [ $c -eq "6" ];do printf ":%02X" $(( $RANDOM % 256 ));let c=c+1;done|sed s/://)

result

$ echo $mac
93:72:71:0B:9E:89
Zibri
  • 291
2
#!/bin/bash
#Creates an array containing all hexadecimal characters
HEX=(a b c d e f 0 1 2 3 4 5 6 7 8 9)
#Defines MAC string length as 0 (total SL will be 17)
SL=0
#Loop sequentially assigns random hex characters in pairs until a full
#MAC address is generated.
while [ $SL -lt 17 ]
do
        num=`shuf -i 0-15 -n 1` #Generates random number which will be used as array index
        RMAC="$RMAC""${HEX[$num]}" #Uses the randomly generated number to select a hex character
        num=`shuf -i 0-15 -n 1` #New random number
        RMAC="$RMAC""${HEX[$num]}" #Appends second hex character
        SL=$[`echo $RMAC | wc -c` - 1] #Calculates SL and stores in var SL
    if [ $SL -lt 17 ] #If string is uncomplete, appends : character
            then
            RMAC=""$RMAC":"
    fi
done
echo $RMAC #Displays randomly generated MAC address
2

This should work

echo 00-60-2f-`openssl rand -hex 3 | sed 's/\(..\)/\1-/g; s/.$//'`
1

This is works in classic shell (#!/bin/sh) script:

random_mac() {
    printf '%.2x\n' "$(shuf -i 0-281474976710655 -n 1)" | sed -r 's/(..)/\1:/g' | cut -d: -f -6
}

Or, if you want to have custom prefix:

random_mac_with_prefix() {
    echo -n "00:60:2f:" &&
    printf '%.2x\n' "$(shuf -i 0-281474976710655 -n 1)" | sed -r 's/(..)/\1:/g' | cut -d: -f -3
}

Example usage:

$ random_mac
96:ef:45:28:45:25
$ random_mac
7e:47:26:ae:ab:d4
$ random_mac_with_prefix 
00:60:2f:24:f4:18
$ random_mac_with_prefix 
00:60:2f:63:08:b2
kvaps
  • 248
1

In Linux:

printf '00-60-2f-' && cut -b 7-11,24-26 /proc/sys/kernel/random/uuid

Explanation:

In Linux /proc/sys/kernel/random/uuid returns a new type 4 (random) UUID every time you read it. Most of its characters are (pseudo)random hexadecimal digits, thus we can use them. E.g:

$ cat /proc/sys/kernel/random/uuid
5501ab12-b530-4db5-a8ea-3df93043f172
$ #           ^    ^       Beware, these characters are not random.
$ #   ^^^^^            ^^^ Let's use characters on these positions.
$ cut -b 7-11,24-26 /proc/sys/kernel/random/uuid
6d-74-a1
$ cut -b 7-11,24-26 /proc/sys/kernel/random/uuid
13-f9-75

Now it's enough to print 00-60-2f- (without newline) first:

$ printf '00-60-2f-' && cut -b 7-11,24-26 /proc/sys/kernel/random/uuid
00-60-2f-86-f9-21

Pros:

  • hexadecimal digits from the very beginning, no conversion/filtering needed;
  • printf and cut are POSIX tools;
  • (for your specific case) hyphens already in UUID, we use them.

Cons:

  • we need to mind the two non-random digits in UUID;
  • /proc/sys/kernel/random/uuid may not be available on some systems;
  • only lowercase letters come so easy (you need additional step to get uppercase letters).
1
end=$( echo $RANDOM | openssl md5 | sed 's/\(..\)/\1-/g' | cut -b-8 )
echo 00-60-2f-$end
0

Another option is to use jot:

echo 00-60-2F-$(jot -w%02X -s- -r 3 0 256)

-w changes the format, -s changes the separator, and -r generates random numbers.

The commands using od in the answers posted by artistoex and zero2cx add extra dashes to the output with OS X's od, but this does not:

echo 00-60-2f-$(od -tx1 -An -N3 /dev/random|awk '$1=$1'|tr \  -)

OS X's od (/usr/bin/od below) uses a different output format than GNU od:

$ /usr/bin/od -N3 -tx1 -An /dev/random|tr ' ' -
-----------c7--fd--55----------------------------------------------------

$ god -N3 -tx1 -An /dev/random|tr ' ' -
-94-9e-5c
nisetama
  • 1,109
0

This one works as well. The output is all in upper case as required.

openssl rand -hex 3 | sed 's/\(..\)\(..\)\(..\)/00-60-2F-\1-\2-\3/' | tr [a-f] [A-F]