Linux bash random number

Random number from a range in a Bash Script

I need to generate a random port number between 2000-65000 from a shell script. The problem is $RANDOM is a 15-bit number, so I’m stuck! PORT=$(($RANDOM%63000+2001)) would work nicely if it wasn’t for the size limitation. Does anyone have an example of how I can do this, maybe by extracting something from /dev/urandom and getting it within a range?

19 Answers 19

Edit: The range is inclusive.

I think shuf is relatively recent — I’ve seen it on Ubuntu systems in the last couple years but not the current RHEL/CentOS.

Also, it’s probably fine for this use, but I believe shuf does actually permute the entire input. This makes it a bad choice if you’re generating the random numbers very frequently.

@Dennis Williamson: Running your test with -n 1 showed negligible time differences, even with end=4000000000 . Good to know shuf works smart, not hard 🙂

@VirenShakya — If you install Homebrew, then you can brew install coreutils . Commands are installed with the prefix g , so it’ll be gshuf .

On Mac OS X and FreeBSD you may also use jot:

In this example, jot has unfair distribution for the interval’s minimum and maximum (i.e., 2000 and 65000). In other words, the min and max will be generated less frequently. See my jot answer for details and a workaround.

According to the bash man page, $RANDOM is distributed between 0 and 32767; that is, it is an unsigned 15-bit value. Assuming $RANDOM is uniformly distributed, you can create a uniformly-distributed unsigned 30-bit integer as follows:

Since your range is not a power of 2, a simple modulo operation will only almost give you a uniform distribution, but with a 30-bit input range and a less-than-16-bit output range, as you have in your case, this should really be close enough:

If I’m understanding this correctly, you are spreading 32,000 numbers amid a range of 1,000,000,000. But they will only hit on multiples of 2^15—you’re skip-counting by 2^15’s, not filling in all digits between 1 and 2^30 evenly, which is what a uniform distribution is.

@isomorphismes Note that the code references $RANDOM twice. On shells that support $RANDOM , a new value is generated every time it is referenced. So this code fills bits 0 through 14 with one $RANDOM value & fills bits 15 through 29 with another. Assuming $RANDOM is uniform & independent, this covers all values from 0 through 2**30-1 without skipping anything.

Читайте также:  Запустить службу печати linux

and here’s one with Python

randport=$(python -S -c "import random; print random.randrange(2000,63000)") 

This one gets an upvote from me. I write bash scripts for various systems and I believe awk is probably the most abundant tool for the job. Worked on mac os x and centos without an issue and I know it’ll work on my debian machine too, and probably any other normal-ish *nix machine.

However, awk’s random seed only seems to refresh once/sec so you might want to a) avoid at all costs or b) re-initialise the seed.

+1 because this seems to be the only POSIX possibility without compilation: RANDOM is not guaranteed by POSIX,

Using the -S option results in ImportError: No module named random . Works if I remove that. Not sure what ghostdog’s intention was for that.

python -S -c «import random; print random.randrange(2000,63000)» seems to work fine. However, when I try to get a random number between 1 and 2, I seem to always get 1. Thoughts?

The simplest general way that comes to mind is a perl one-liner:

perl -e 'print int(rand(65000-2000)) + 2000' 

You could always just use two numbers:

PORT=$(($RANDOM + ($RANDOM % 2) * 32768)) 

You still have to clip to your range. It’s not a general n-bit random number method, but it’ll work for your case, and it’s all inside bash.

If you want to be really cute and read from /dev/urandom, you could do this:

od -A n -N 2 -t u2 /dev/urandom 

That’ll read two bytes and print them as an unsigned int; you still have to do your clipping.

I used this technique and notice that now and then there will be no number generated, simply blank space.

It requires perl installed. I write a script which should run on most if not all linux machines, sticking with awk version from another answer

Adding random numbers favours middle outcomes at the expense of low or high. It is not uniformly random.

@isomorphismes Yes, if you’re literally just adding two random numbers. But, assuming you’re referring to the second expression here, that’s not what it’s doing. It’s a random number in [0,32767] plus an independent random choice for the next bit, i.e. 0 or 32768. It’s uniform. (It’s not ideal for the original question though since you have to clip the range with rerolling.)

If you’re not a bash expert and were looking to get this into a variable in a Linux-based bash script, try this:

Читайте также:  Linux cnc токарная версия

That gets you the range of 200 to 700 into $VAR , inclusive.

Here’s another one. I thought it would work on just about anything, but sort’s random option isn’t available on my centos box at work.

 seq 2000 65000 | sort -R | head -n 1 
echo $(ruby -e 'puts rand(20..65)') #=> 65 (inclusive ending) echo $(ruby -e 'puts rand(20. 65)') #=> 37 (exclusive ending) 

Bash documentation says that every time $RANDOM is referenced, a random number between 0 and 32767 is returned. If we sum two consecutive references, we get values from 0 to 65534, which covers the desired range of 63001 possibilities for a random number between 2000 and 65000.

To adjust it to the exact range, we use the sum modulo 63001, which will give us a value from 0 to 63000. This in turn just needs an increment by 2000 to provide the desired random number, between 2000 and 65000. This can be summarized as follows:

port=$((((RANDOM + RANDOM) % 63001) + 2000)) 
# Generate random numbers and print the lowest and greatest found test-random-max-min() < max=2000 min=65000 for i in ; do port=$((((RANDOM + RANDOM) % 63001) + 2000)) echo -en "\r$port" [[ "$port" -gt "$max" ]] && max="$port" [[ "$port" -lt "$min" ]] && min="$port" done echo -e "\rMax: $max, min: $min" > # Sample output # Max: 64990, min: 2002 # Max: 65000, min: 2004 # Max: 64970, min: 2000 

Correctness of the calculation

Here is a full, brute-force test for the correctness of the calculation. This program just tries to generate all 63001 different possibilities randomly, using the calculation under test. The —jobs parameter should make it run faster, but it’s not deterministic (total of possibilities generated may be lower than 63001).

test-all() < start=$(date +%s) find_start=$(date +%s) total=0; ports=(); i=0 rm -f ports/ports.* ports.* mkdir -p ports while [[ "$total" -lt "$2" && "$all_found" != "yes" ]]; do port=$((((RANDOM + RANDOM) % 63001) + 2000)); i=$((i+1)) if [[ -z "$" ]]; then ports["$port"]="$port" total=$((total + 1)) if [[ $((total % 1000)) == 0 ]]; then echo -en "Elapsed time: $(($(date +%s) - find_start))s \t" echo -e "Found: $port \t\t Total: $total\tIteration: $i" find_start=$(date +%s) fi fi done all_found="yes" echo "Job $1 finished after $i iterations in $(($(date +%s) - start))s." out="ports.$1.txt" [[ "$1" != "0" ]] && out="ports/$out" echo "$" > "$out" > say-total() < generated_ports=$(cat "$@" | tr ' ' '\n' | \sed -E s/'^(8)$'/'0\1'/) echo "Total generated: $(echo "$generated_ports" | sort | uniq | wc -l)." > total-single() < say-total "ports.0.txt"; >total-jobs() < say-total "ports/"*; >all_found="no" [[ "$1" != "--jobs" ]] && test-all 0 63001 && total-single && exit for i in ; do test-all "$i" 40000 & sleep 1; done && wait && total-jobs 

For determining how many iterations are needed to get a given probability p/q of all 63001 possibilities having been generated, I believe we can use the expression below. For example, here is the calculation for a probability greater than 1/2, and here for greater than 9/10.

Читайте также:  Ksc web console linux

Источник

Generate Random Number in Bash

Generate Random Number in Bash

  1. Using the $RANDOM Variable to Generate Random Numbers
  2. Using the shuf Command to Generate Random Numbers
  3. Using /dev/urandom to Generate Random Numbers
  4. Using Awk to Generate Random Numbers

In Bash, an integer or float can be used to produce a random number. A bash script may be used to create a random number within a given range or size. This article demonstrates various methods for generating random numbers in Bash.

Using the $RANDOM Variable to Generate Random Numbers

The $RANDOM variable can be used to produce a random number or a range of random values between 0 and 32767 . However, you may limit the number range used to generate random numbers by dividing the $RANDOM value by a specific value.

To produce a random number between 0 and 32767, use the following command:

You may produce a random number from a specific range by dividing the $RANDOM variable by the remaining value. It contains double first parentheses (()) with a $ symbol.

To produce a random number between 1 and 100, use the following command:

Since the percentage sign causes the mathematical operation modulo to fail, the number 100 is never attained (remainder after division). If the random generator returned the value 100, the modulo operation would provide the value 0.

Using the shuf Command to Generate Random Numbers

You may use the command shuf to produce numerous numbers in a shell script.

The following command will create a single integer between 0 and 100:

In the above command, -i denotes the input range, and -n denotes the headcount. The key -n is followed by any positive number, indicating the number to generate random numbers. Run the following command to print five random numbers between 0 and 100.

Using /dev/urandom to Generate Random Numbers

The /dev/urandom device file allows you to produce pseudo-random numbers far more random than the $RANDOM variable. However, putting these numbers into variables in a script involves further work, such as filtering via od , as shown in the example.

od /dev/urandom -A n -t d -N 1 

Источник

Оцените статью
Adblock
detector