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