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?

3
  • 10
    echo -n 00-60-2F; dd bs=1 count=3 if=/dev/random 2>/dev/null |hexdump -v -e '/1 "-%02X"'
    – wnrph
    Jan 20, 2012 at 13:56
  • 1
    @artistoex That was beautiful... Why did you not post that as the answer?
    – bobmagoo
    Aug 25, 2012 at 0:22
  • If you're getting "Invalid MAC Address" or "SIOCSIFHWADDR: Cannot assign requested address" errors then you might need to make sure you're generating a unicast MAC address. Try this answer: stackoverflow.com/a/42661696/1456173 Feb 8, 2021 at 12:46

15 Answers 15

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.
4
  • 2
    As user58033 rightfully pointed out in an answer (which might be gone now): %02X would give one upper case letters.
    – Arjan
    Dec 5, 2010 at 14:17
  • Ah, good point. I spaced that the original question gave an example using upper case. Thanks for the welcome. :-) Dec 5, 2010 at 20:23
  • Extra +1 if you can explain what is happening and why your solution works. Jun 25, 2011 at 17:33
  • There you go, @Jed. Jul 7, 2011 at 1:18
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.

4
  • thanks for realizing that i was trying to make him learn to fish instead of spoon-feeding a solution Dec 5, 2010 at 20:16
  • 1
    You could always explain your answer so that you are both teaching him how to fish in addition to providing one this time around... Jun 25, 2011 at 17:34
  • This script runs considerably much faster than the one proposed by hbdgaf and does not generate any invalid MAC address in a 1000 times loop. Thanks! Mar 21, 2015 at 20:53
  • 1
    Your solution works and has only 3 lines. I'm sold. Apr 6, 2018 at 13:07
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.

6
  • Extra +1 if you provide an example of each (I know you are teaching him how to fish, but you could give him the pieces of the puzzle to put together, and explain how/why they work). Jun 25, 2011 at 17:35
  • @Jed Daniels - done and done. code provided. Jun 26, 2011 at 18:28
  • This code is generating a few invalid MACs for me. Looping it 1000 times I got a few MACs like 00-60-2F-8B-5-2C, 00-60-2F-A-71-97, 00-60-2F-82-F1-4. Mar 21, 2015 at 20:49
  • It was just something I threw out there since the OP was insisting on bash. You're saying you can't figure out how to pad the single digits with a zero right? @BrunoFinger Mar 22, 2015 at 3:33
  • Could be easily reworked to use 16 instead of 255 and generate the zero placeholder. It's just more lines... Mar 22, 2015 at 3:51
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.

1
  • Can you describe in your answer what od exactly does? It would help to use it in other ways too! Oct 12, 2016 at 6:42
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
  • It seems to need IFS= read … to avoid folding 09 0a and 20 (the usual IFS chars) into 00. Dec 4, 2010 at 20:37
  • @Chris: Sorry, I took a couple of things out while doing some double checking and forgot to put them back in. It also needs -d ''. I'll fix my answer. Thanks for letting me know. Dec 4, 2010 at 20:48
  • Oops, the -r that protects ` \ ` fell out. I wish proper handling of binary data in shell programs was not so fiddly. ☺ It seems impossible to accurately represent 00 in the middle of the string. Your single-character-at-a-time method handles 00 by virtue of convenient (designed?) cooperation between read, string interpolation and how printf treats the one-character argument '. Sigh. Dec 4, 2010 at 21:21
  • @Chris: I wish my DWIM processor wasn't on the fritz. By the way, you might be interested to see a pure Bash script I wrote that duplicates the core functionality of hexdump -C. Dec 4, 2010 at 21:58
  • Extra +1 if you can explain what is happening and why your solution works. Jun 25, 2011 at 17:33
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

2
  • Unix effectiveness at work :-)
    – wnrph
    Sep 6, 2012 at 9:40
  • 1
    hexdump -n3 -e'/3 "00-60-2F" 3/1 "-%02X"' /dev/random is a little shorter :-)
    – wnrph
    Sep 7, 2012 at 8:18
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.

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
end=$( echo $RANDOM | openssl md5 | sed 's/\(..\)/\1-/g' | cut -b-8 )
echo 00-60-2f-$end
1
  • 5
    Extra +1 if you can explain what is happening and why your solution works. Jun 25, 2011 at 17:35
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
1

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
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]
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
2
  • 1
    Beware with jot. I have some mac generated this way that have "100" in them, eg 00-60-2F-100-A3-F6...
    – petrus
    Sep 14, 2018 at 14:54
  • @petrus You're right, I edited the answer to change jot -w%02X -s- -r 3 1 256 to jot -w%02X -s- -r 3 0 256.
    – nisetama
    Dec 7, 2018 at 16:41
0

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).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .