Sh random number between range

How can I generate a random number between 0-60 in sh (/ bin / sh, not bash)? This is a satellite box, there is no variable $RANDOM and other products [cksum, od (od -vAn -N4 -tu4 </ dev / urandom)].

I want to randomize crontab runtime.

+4
source share
6 answers

If you have tr, head and / dev / urandom, you can write this:

 tr -cd 0-9 </dev/urandom | head -c 3 

Then you need to use the remainder operator to place the range 0-60.

+6
source

How about using nanoseconds of system time?

 date +%N 

It doesn't seem like you need cryptographically useful numbers here.

Depending on the version of /bin/sh this is possible:

$ (( date +%N % 60))

If it does not support the syntax $(()) , but you have dc, you can try:

 dc -e `date +%N`' 60 % p' 

Not knowing which operating system, version of /bin/sh or what tools are available, it is difficult to find a solution, guaranteed operation.

+8
source

Do you have awk? You can call the awk rand () function. For instance:

 awk 'BEGIN { printf("%d\n",rand()*60) }' < /dev/null 
+2
source

I know this post is old, but the suggested answers do not generate uniform, unbiased random numbers. The accepted answer is basically this:

 % echo $(( $(tr -cd 0-9 </dev/urandom | head -c 3) % 60)) 

The problem with this proposal is that, choosing a 3-digit number from /dev/urandom , the range is from 0 to 999, only 1000 numbers. However, 1000 is not evenly divided by 60. Thus, you will be biased towards the creation of 0-959 a little more than 960-999.

The second answer, while creative in using nanoseconds from your watch, suffers from the same biased approach:

 % echo $(( $(date +%N) % 60 )) 

The nanosecond range is 09999999999, which is 1 billion numbers. So, if you divide this result by 60, you will again be biased towards creating 0-999,999,959 a little more than 999,999,960-999,999,999.

All other answers are one-sided uneven generation.

To create unbiased uniform random numbers in the range 0-59 (this is what I assume it means, not 0-60 if it tries to randomize the crontab(1) record), we need to force the output to be a multiple of 60.

First we create a random 32-bit number between 0 and 4294967295:

 % RNUM=$(od -An -N4 -tu2 /dev/urandom | awk '{print $1}') 

Now we will force our range to be between $ MIN and 4294967295, which is a multiple of 60:

 % MIN=$((2**32 % 60)) # 16 

It means:

 4294967296 - 16 = 4294967280 4294967280 / 60 = 71582788.0 

In other words, my range [16, 4294967295] is exactly a multiple of 60. Thus, every number that I generate in this range, then dividing by 60, will be equally probable, like any other number. So I have an unbiased number generator 0-59 (or 1-60 if you add 1).

It remains only to make sure that my number is between 16 and 4294967295. If my number is less than 16, I need to create a new number:

 % while [ $RNUM -lt $MIN ]; do RNUM=$(od -An -N1 -tu2 /dev/urandom); done % MINUTE=$(($RNUM % 60)) 

Everything is ready for copying / pasting goodnees:

 #!/bin/bash RNUM=$(od -An -N4 -tu2 /dev/urandom | awk '{print $1}') MIN=$((2**32 % 60)) while [ $RNUM -lt $MIN ]; do RNUM=$(od -An -N1 -tu2 /dev/urandom); done MINUTE=$(($RNUM % 60)) 
+1
source
  value = `od -An -N2 -tu2 / dev / urandom`
 minutes = `expr $ value% 60` 

The seed will be between 0 and 65535, which is not an equal multiple of 60, so minutes 0-15 have a slightly greater chance of choice, but the discrepancy is probably not important.

If you want to achieve perfection, use "od -An -N1 -tu1" and a loop until the value is less than 240.

Tested with busybox od.

0
source

Marco's answer will fail if the generated number starts at 0 and has other digits greater than 7, since it is interpreted as octal, I would suggest:

 tr -cd 0-9 </dev/urandom | head -c 4 | sed -e 's/^00*// 

specifically, if you want to process it further, for example, to set the range:

 RANDOM=`tr -cd 0-9 </dev/urandom | head -c 4 | sed -e 's/^00*//'` RND50=$((($RANDOM%50)+1)) // random number between 1 and 50 
0
source

Source: https://habr.com/ru/post/1335305/


All Articles