122

After googling a bit I couldn't find a simple way to use a shell command to generate a random decimal integer number included in a specific range, that is between a minimum and a maximum.

I read about /dev/random, /dev/urandom and $RANDOM, but none of these can do what I need.

Is there another useful command, or a way to use the previous data?

BowPark
  • 4,895
  • 3
    See this SO Q&A: http://stackoverflow.com/questions/8988824/generating-random-number-in-bash-shell-script as well as this http://stackoverflow.com/questions/2556190/random-number-from-a-range-in-a-bash-script. – slm Jul 04 '14 at 13:34
  • 1
    Possibly related: http://unix.stackexchange.com/questions/42045/random-number-needed/42046#42046 – slm Jul 04 '14 at 14:09
  • Related: http://unix.stackexchange.com/questions/124478/how-to-randomize-the-output-from-seq – slm Jul 04 '14 at 14:12

13 Answers13

198

You can try shuf from GNU coreutils:

shuf -i 1-100 -n 1
cuonglm
  • 153,898
64

In the POSIX toolchest, you can use awk:

awk -v min=5 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

Do not use that as a source to generate passwords or secret data for instance, as with most awk implementations, the number can easily be guessed based on the time that command was run.

With many awk implementations, that command run twice within the same second will generally give you the same output.

  • 1
    +1.. As used in a script to assign output to a variable (w/o introducing a newline character and using zero padding for two places): x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}') – Christopher Apr 22 '19 at 18:35
  • 3
    It's based on time? since I got the same value in 2 consecutive runs... – Shai Alon May 05 '19 at 14:13
  • @ShaiAlon the current time is often used as the default seed value for srand() so usually, yes it's based on time, see Stéphane's sentence about that in his answer. You can get around that by changing the initial seed to a random number with awk -v min=5 -v max=10 -v seed="$(od -An -N4 -tu4 /dev/urandom)" 'BEGIN{srand(seed+0); print int(min+rand()*(max-min+1))}'. You could use $RANDOM instead of od ... /dev/random but then your seed value is in the relatively small range of 0 to 32767 and so you'll probably get noticeable repetitions in your output over time. – Ed Morton Aug 09 '19 at 17:54
  • I've been burned too many times by various awk implementations' bad randomization to trust them, which is why my answer (also portable and pure posix) uses od on /dev/urandom – Adam Katz Mar 21 '24 at 03:17
46

jot

On BSD and OSX you can use jot to return a single random (-r) number from the interval min to max, inclusive.

$ min=5
$ max=10
$ jot -r 1 $min $max

Distribution problem

Unfortunately, the range and distribution of randomly generated numbers is influenced by the fact that jot uses double precision floating point arithmetic internally and printf(3) for output format, which causes rounding and truncation issues. Therefore, the interval's min and max are generated less frequently as demonstrated:

$ jot -r 100000 5 10 | sort -n | uniq -c
9918  5
20176 6
20006 7
20083 8
19879 9
9938  10

On OS X 10.11 (El Capitan) this appears to have been fixed:

$ jot -r 100000 5 10 | sort -n | uniq -c
16692 5
16550 6
16856 7
16579 8
16714 9
16609 10  

and...

$ jot -r 1000000 1 10 | sort -n | uniq -c
100430 1
99965 2
99982 3
99796 4
100444 5
99853 6
99835 7
100397 8
99588 9
99710 10

Solving the distribution problem

For older versions of OS X, fortunately there are several workarounds. One is to use printf(3) integer conversion. The only caveat is that the interval maximum now becomes max+1. By using integer formatting, we get fair distribution across the entire interval:

$ jot -w %i -r 100000 5 11 | sort -n | uniq -c
16756 5
16571 6
16744 7
16605 8
16683 9
16641 10

The perfect solution

Finally, to get a fair roll of the dice using the workaround, we have:

$ min=5
$ max_plus1=11  # 10 + 1
$ jot -w %i -r 1 $min $max_plus1

Extra homework

See jot(1) for the gory math and formatting details and many more examples.

18

The $RANDOM variable is normally not a good way to generated good random values. The output of /dev/[u]random need also to be converted first.

An easier way is to use higher level languages, like e.g. python:

To generate a random integer variable between 5 and 10 (5<=N<=10), use

python -c "import random; print random.randint(5,10)"

Do not use this for cryptographic applications.

jofel
  • 26,758
  • This was useful. Thank you very much :) As signed often has worked with Unix->Solaris 10 the GNU utils are not integrated by default. This worked however. – propatience Feb 22 '17 at 10:29
16

You can get the random number through urandom

head -200 /dev/urandom | cksum

Output:

3310670062 52870

To retrieve the one part of the above number.

head -200 /dev/urandom | cksum | cut -f1 -d " "

Then the output is

3310670062

zangw
  • 331
  • 4
  • 5
  • 4
    head -200 (or its POSIX equivalent head -n 100) returns the first 200 lines of /dev/urandom. /dev/urandom is not a text file, that command could return from 200 bytes (all 0x0a ones, LF in ASCII) to infinity (if the 0xa byte happens never to be returned) but values between 45k and 55k being the most likely. cksum returns a 32bit number, so it's pointless to get more than 4 bytes from /dev/urandom. – Stéphane Chazelas Sep 10 '19 at 11:30
  • So why not dd bs=200 count=1 2>/dev/null to obtain EXACTLY 200 bytes of random data, instead of head? –  Oct 22 '21 at 04:07
  • For bash compatible i.e.<0, 32767>: $(($(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -d' ' -f1) % 32768)). Source: https://mdk.fr/blog/bashism-how-to-generate-random-number-without-random.html – pevik Mar 11 '22 at 12:26
  • As @StéphaneChazelas noted, cksum's most significant bit can be limited in size because it yields values from 0-4294967295 (2³²-1). Consider head -c200 /dev/urandom |cksum |awk '{printf "%d", $1 % 10e8}' instead, which grabs 200 random bytes rather than lines and provides random numbers from 0 to 999999999. To specify a maximum size up to 4294967295, change the modulo to that value plus one, like head -c200 /dev/urandom |cksum |awk '{printf "%d", $1 % 256}' for random numbers from 0-255. – Adam Katz Jan 06 '24 at 22:05
  • Oops! -c isn't in Posix head, so you'll have to use dd as noted above. – Adam Katz Jan 06 '24 at 22:17
14

To generate a random integer variable between 5 and 10 (including both), use

echo $(( RANDOM % (10 - 5 + 1 ) + 5 ))

% works as a modulo operator.

There are probably better ways to convert the random variable $RANDOM to a specific range. Do not use this for cryptographic applications or in cases you need real, equal-distributed random variables (like for simulations).

jofel
  • 26,758
  • 7
    In many shell implementations that do have $RANDOM (especially older ones, especially bash), that's not very random. In most shells, the number is between 0 and 65535, so any range whose width is not a power of two will have probability distribution discrepency. (in this case, numbers 5 to 8 will have a probability of 10923/65536, while 9 and 10 will have a probability of 10922/65536). The wider the range, the bigger the discrepency. – Stéphane Chazelas Jul 04 '14 at 12:39
  • 1
    Sorry, that's 32767 not 65535, so the calculation above is wrong (and it's actually worse). – Stéphane Chazelas Jul 04 '14 at 13:05
  • @StéphaneChazelas I had this in mind as I wrote that there are better ways... – jofel Jul 04 '14 at 13:27
8

# echo $(( $RANDOM % 256 )) will produce a "random" number between 0-255 in modern *sh dialects.

7

Maybe the UUID (on Linux) can be used to retrieve the random number

$ cat /proc/sys/kernel/random/uuid
cdd52826-327d-4355-9737-895f58ad11b4

To get the random number between 70 and 100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid
zangw
  • 331
  • 4
  • 5
6
cat /dev/urandom | tr -dc 'a-fA-F0-9' | fold -w 8 | head -n 1

This will generate an 8 digits long hexadecimal number.

Mathieu
  • 2,719
5

Distribution is good:

for (( i = 1 ; i <= 100000 ; i++ )) do echo $(( RANDOM % (20 - 10 + 1 ) + 10 )) ; done | sort -n | uniq -c

Gives:

  count value

9183 10 9109 11 8915 12 9037 13 9100 14 9138 15 9125 16 9261 17 9088 18 8996 19 9048 20

Greenonline
  • 1,851
  • 7
  • 17
  • 23
SteveK
  • 51
2

To stay entirely within bash and use the $RANDOM variable but avoid the uneven distribution:

#!/bin/bash
range=10 
floor=20

if [ $range -gt 32768 ]; then echo 'range outside of what $RANDOM can provide.' >&2 exit 1 # exit before entering infinite loop fi

max_RANDOM=$(( 2*15/$range$range )) r=$RANDOM until [ $r -lt $max_RANDOM ]; do r=$RANDOM done echo $(( r % $range + $floor ))

This example would provide random numbers from 20 through 29.

Explanation: Any $RANDOM result greater than 2**15/$range*$range is in an incomplete modulus fold within which the result of $r % $range has a max value of 2**15%$range, so results from 0 to 2**15%$range have weight of 2**15/$range+1 and the value above have a weight of just 2**15/$range. The worst case scenario of the poor distribution is when the 2**15/$range is 1 and 2**15%$range is not 0. Here, we can see clearly the problem when the range is 2/3 of 2**15, where half the results have twice the probability:

$ range=$((2**15*2/3)); for (( i=0; i<10000; i++)); \
> do [ $((RANDOM%range) -le $((range/2)) ] \
> && ((le_10922++)) || ((gt_10922++)) ; done; \
> echo le_10922 $le_10922; echo gt_10922 $gt_10922 
le_10922 6748
gt_10922 3253
2

Using RANDOM

MAX=999999                      #Upper Range
MIN=100000                      #Lower Range
DIFF=$((MAX-MIN+1))             #+1 to inlcude upper limit
R=$(($(($RANDOM%$DIFF))+MIN))

Using SHUF

MIN=1000
MAX=9999
COUNT=1                         #count of Random Numbers to be generated
shuf -i $MIN-$MAX -n $COUNT
shuf -i 1000-9999 -n 1

Using /dev/random

od -An -N2 -i /dev/random       #Generates a 2 byte Number, -N2=Generates 2 Random Number btween range 0-65535
1

The best way to do this portably is with od (there's no way to do it in portable POSIX shell without an external command, but at least this solution only has one such call). Here's a complete function you can use:

# Usage: random [[MIN] MAX]
# Display a random number from MIN (def=0) to MAX (def=2^32-1) (inclusive)
random() {
  n=$(od -An -N4 -tu /dev/urandom) || return $?
  case $# in
    ( 0 ) echo $n ;;                                  # min=0  max=4294967295
    ( 1 ) echo $(( n % ($1 + 1) )) ;;                 # min=0  max=$1
    ( 2 ) echo $(( n % ($2 + 1 - $1) + $1 )) ;;       # min=$1 max=$2
  esac
} 

This generates a random unsigned integer and scopes it to optionally specified minimum and maximum values.

Examples:

$ random      # a random integer between 0 and 4294967295 (2³²-1)
102618
$ random 5    # a random integer between 0 and 5
5
$ random 1 5  # a random integer between 1 and 5
3

We all love one-liners, right? I managed to wedge this into 80 characters:

random(){ echo $(($(od -An -N4 -tu /dev/random)${1+%(${2+$2- }$1+1)${2++$1}}));}

(Explanation removed for being too verbose, see revision 3 for detail.)

Adam Katz
  • 3,965