436 lines
18 KiB
HTML
436 lines
18 KiB
HTML
<!--startcut ======================================================= -->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<html>
|
|
<head>
|
|
<META NAME="generator" CONTENT="lgazmail v1.3D.k">
|
|
<TITLE>The Answer Guy 55: Getting Random Values in sh</TITLE>
|
|
</HEAD><BODY BGCOLOR="#FFFFFF" TEXT="#000000"
|
|
LINK="#3366FF" VLINK="#A000A0">
|
|
<!-- ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
<P> <hr>
|
|
<!-- *** BEGIN navbar *** :::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
<p align="center">
|
|
<A HREF="../lg_bytes55.html"><IMG ALT="[ Prev ]"
|
|
SRC="../../gx/navbar/prev.jpg"
|
|
WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<IMG ALT="" SRC="../../gx/navbar/left.jpg"
|
|
WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom" >
|
|
<A HREF="../index.html"><IMG ALT="[ Table of Contents ]"
|
|
SRC="../../gx/navbar/toc.jpg"
|
|
WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A>
|
|
<A HREF="../../index.html"><IMG ALT="[ Front Page ]"
|
|
SRC="../../gx/navbar/frontpage.jpg"
|
|
WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<A HREF="../../faq/index.html"><IMG ALT="[ FAQ ]"
|
|
SRC="../../gx/navbar/faq.jpg"
|
|
WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<IMG ALT="" SRC="../../gx/navbar/right.jpg"
|
|
WIDTH="15" HEIGHT="45" ALIGN="bottom" >
|
|
<A HREF="../lg_tips55.html"><IMG ALT="[ Next ]"
|
|
SRC="../../gx/navbar/next.jpg"
|
|
WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A>
|
|
<!-- *** END navbar *** :::::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
</p>
|
|
<P> <hr> <P>
|
|
<!-- ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
<center>
|
|
<H1><A NAME="answer">
|
|
<img src="../../gx/dennis/qbubble.gif" alt="(?)"
|
|
border="0" align="middle">
|
|
<font color="#B03060">The Answer Guy</font>
|
|
<img src="../../gx/dennis/bbubble.gif" alt="(!)"
|
|
border="0" align="middle">
|
|
</A></H1>
|
|
<BR>
|
|
<H4>By James T. Dennis,
|
|
<a href="mailto:linux-questions-only@ssc.com">linux-questions-only@ssc.com</a><BR>
|
|
LinuxCare,
|
|
<A HREF="http://www.linuxcare.com/">http://www.linuxcare.com/</A>
|
|
</H4>
|
|
</center>
|
|
|
|
<p><hr><p>
|
|
<!-- endcut ======================================================= -->
|
|
<!-- begin 4 -->
|
|
<H3 align="left"><img src="../../gx/dennis/qbubble.gif"
|
|
height="50" width="60" alt="(?) " border="0"
|
|
>Getting Random Values in sh</H3>
|
|
|
|
|
|
<p><strong>From Devil Man on Thu, 08 Jun 2000
|
|
</strong></p>
|
|
<!-- ::
|
|
Getting Random Values in sh
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
:: -->
|
|
<P><STRONG>
|
|
Hello answer guy I was wondering and have been unable to find any info about a
|
|
shell scripting utility or command that can be used to generate a random number
|
|
such as if I wanted to create a shell script to generate a random number
|
|
between 1-20 or so. It dose not have to be a all in one basically how do you
|
|
generate random numbers and the command line?
|
|
</STRONG></P>
|
|
<P><STRONG>
|
|
Thanks randomly speaking
|
|
</STRONG></P>
|
|
<BLOCKQUOTE><IMG SRC="../../gx/dennis/bbub.gif" ALT="(!)"
|
|
HEIGHT="28" WIDTH="50" BORDER="0"
|
|
>
|
|
Well the easiest way, under bash is to simply use the predefined
|
|
"magic" shell variable: $RANDOM. So the following might work for
|
|
you:
|
|
</BLOCKQUOTE>
|
|
<pre><BLOCKQuote>
|
|
RANDOM=$$$(date %+s)
|
|
function d20 () {
|
|
d=$[ ( $RANDOM % 20 ) + 1 ]
|
|
}
|
|
</BLOCKQUOTE></pre>
|
|
<BLOCKQUOTE>
|
|
... The first line just seeds bash' random number generator using
|
|
your current process ID (PID) and the current time/date expressed
|
|
as the number of seconds since 1970 (the UNIX epoch). This should
|
|
prevent RANDOM from generating the same predictable sequence every
|
|
time you run it. (You can set bash' RANDOM to new seed values,
|
|
but if you ever 'unset' it --- it will lose its special "magic"
|
|
property for the life of that shell/process. This is true of a
|
|
couple of bash' "magic" variables).
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Note that this form of random seeding is common but not adequate
|
|
for proper cryptography, or high stakes gambling. For that we
|
|
probably wouldn't be using the shell, we certainly wouldn't be
|
|
storing things in environment variables, and we'd probably want to
|
|
read a bit of entropy out of the Linux <TT>/dev/urandom</TT> or <TT>/dev/random</TT>
|
|
devices (depending on the relative importance of speed versus
|
|
"quality of entropy" required).
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Shell function, which I've named after the gamer's conventional
|
|
abbreviation for their favorite polyhedral (dice), simply takes a
|
|
$RANDOM value modulo 20 (modulus is the remainder of a division,
|
|
and thus gives us a number between 0 and 19) and then I add one
|
|
to just this from the range 0-19 up to 1-20.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
This method (take a modulus of a number and add a base) is
|
|
commonly used by programmers to get random values within a specific
|
|
range. If you want the numbers to follow a specific curve
|
|
you can use additional arithmetic operations and additional random
|
|
values. For example to get a nice bell curve that reasonably
|
|
approximates a natural population where lots of entities are
|
|
"average", a few are "exceptional" or "bad" and a very few are
|
|
"super" or "woeful" you can use a sum of several random numbers.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
The classic "Dungeons & Dragons (TM)" 3d6 gives such a curve
|
|
which is why they don't simply use a single d20 for each ability
|
|
score. It's also why simple percentile rolling on a pair of
|
|
d10s or d20s doesn't give the "right" distribution of results.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
You can get some really wacky curves if you take one random
|
|
value and divide it with another (round down to the nearest
|
|
int). For example a d6/d4 gives a number from 0 to 6 with
|
|
only a 1 in 24 chance of getting a 6, and 25% chance of getting
|
|
nothing, just over %30 of getting a 1, etc. But I digress.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Of course my example here depends on bash. So it's not very
|
|
portable.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Here's a method that's somewhat more portable:
|
|
</BLOCKQUOTE>
|
|
<pre><BLOCKQuote>
|
|
r=`fortune | cksum | cut -f1 -d" "`
|
|
d=`expr \( $r % 20 \) + 1 `
|
|
</BLOCKQuote></pre>
|
|
<BLOCKQUOTE>
|
|
Those are shell backtick (command substitution) operators. That's
|
|
an older syntax which is supported by very old shells (and is still
|
|
supported by new ones). I use that on the command line sometimes,
|
|
but I prefer to use the newer syntax $( ... ) in scripts and when
|
|
explaining shell programming. It's easier to read and it's easier
|
|
to write clearly on a whiteboard. (Of course both forms mean the
|
|
same thing, execute the enclosed command(s) capturing the output
|
|
from them, and paste that output into parent expression as a
|
|
replace for the whole "backtick" expression).
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
The 'fortune' command is included with most versions of UNIX and is
|
|
commonly installed. It's just a little program that randomly
|
|
chooses a "fortune cookie" --- a random quotation or aphorism ---
|
|
and prints it. Lots of people see those every time they log in,
|
|
and some of the X screensavers (like Mr Nose) use them.
|
|
In this case we get a random phrase and feed it to the cksum
|
|
(BSD/SysV checksum program). The checksum of a random strings
|
|
should be random. (I don't have a rigorous mathematical proof of
|
|
that handy --- but I'm pretty sure it's true; though it may not
|
|
give a very even distribution). (That's another advantage to
|
|
the $(...) form. It's nestable, you can have $( foo $( bar ... ) )
|
|
without ambiguity or error).
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
So I use another line and the old 'expr' command to scale $r to the
|
|
desired range. I have to use two lines in this case, since the old
|
|
"backtick" form cannot be "nested" (or at least the kinds of
|
|
quoting tricks that might allow one to nest such a beast would
|
|
probably not be very portable and would certainly be less readable.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Note that the 'expr' command is fairly picky --- so we must
|
|
separate our operands and operators with spaces so that it sees
|
|
each as a separate command line argument. Also note that I must
|
|
quote/escape the parentheses in my arguments to 'expr' since I need
|
|
for 'expr' to see them, so I have to prevent the shell
|
|
(specifically the subshell that's executing my backtick command)
|
|
from seeing those parentheses as a "subshell" operator. You could
|
|
also wrap each of those parens. in quotes, single (hard) or double
|
|
(soft). However you should NOT try to just wrap the whole
|
|
expression in single or double quotes, because then 'expr' will see
|
|
it all as one big (string) argument rather than as a sequence of
|
|
numbers and operators. Sorry that's so complicated. That's how
|
|
'expr' works. In general it's much easier to use a more recent
|
|
ksh, bash, or zsh which supports the internal 'let' command as well
|
|
as $(( .... )) and/or $[ ... ] syntax for arithmetic operations.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Obviously if you want you script to be very portable, and you can't
|
|
guarantee that your users will have a 'fortune' command installed,
|
|
or that they'll have a recent version of a decent shell then you'll
|
|
have to work at some other way to get a random number.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
As long as you have expr, cksum, and ps (and/or w and/or who), date
|
|
(and/or the time command), cut (or awk) it should be possible to
|
|
cook up small random numbers suitable for dice games, etc.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
The trick is to run some of those commands in a subshell, piping
|
|
their combined output into cksum and cut out the checksum value.
|
|
Any commands that are very likely to give different, even slightly
|
|
different information when run from one second to the next are
|
|
suitable as input to your checksum. Thus one new process or
|
|
one that dies or changes state give different ps output. Every
|
|
second the idle time reported by the 'w' (who) command will be
|
|
updated. Of course the 'date' command will be different every
|
|
second, as well.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Of course once you have a seed value (based on something
|
|
non-deterministic, or something that is usually going to be
|
|
different each time your program runs) then you can use
|
|
your own arithmetic operations to perturb that seed value.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Here's a link to a discussion of "simple psuedo-random
|
|
number generation":
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE><BLOCKQuote>
|
|
<A HREF="http://www.sct.gu.edu.au/~anthony/info/C/RandomNumbers"
|
|
>http://www.sct.gu.edu.au/~anthony/info/C/RandomNumbers</A>
|
|
</BLOCKQuote></BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
The examples can be adapted to sh pretty easily:
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Set initial value:
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE><BLOCKQUOTE><CODE>
|
|
seed=`( echo $$ ; time ps ; w ; date ) | cksum | cut -f1 -d" " `
|
|
</CODE></BLOCKQUOTE></BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Use it:
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE><BLOCKQUOTE><CODE>
|
|
echo $seed
|
|
<BR>seed=` expr \( $seed \* 9301 + 4929 \) % 233280 `
|
|
</CODE></BLOCKQUOTE></BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
... note I have to escape my "*" 'expr' operator to prevent it
|
|
from being expanded (into a list of files) due to shell globbing.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Also note that this must be run in the current shell context
|
|
--- putting the seed=... line in a shell script wouldn't work
|
|
because the shell script runs in its own shell, updates its own
|
|
value of the seed, and then exits. That would leave our copy of
|
|
the seed unchanged.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
So, if this calculation (the linear congruential method) is to be
|
|
stored in a shell script it must be invoked with the shell's "dot
|
|
operator" or the 'source' built-in command. That will execute it
|
|
within the context of the current shell, allowing the lines therein
|
|
to modify the values of your current shell's variables.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
I came across another nice article on the "linear congruential"
|
|
calculation of psuedo-random numbers at:
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE><BLOCKQuote>
|
|
<A HREF="http://www.acm.inf.ethz.ch/ProblemSetArchive/B_US_NorthCen/1996/prob_f.html"
|
|
>http://www.acm.inf.ethz.ch/ProblemSetArchive/B_US_NorthCen/1996/prob_f.html</A>
|
|
</BLOCKQuote></BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
This apparently was in the context of a programming assignment,
|
|
challenge or contest of some sort.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
It should be noted that the values of your L, I, and M (the numbers
|
|
you multiple, increment and modulo your current/seed value with
|
|
at each iteration) can't be arbitrarily chosen. There are some
|
|
some values for these that give "good" psuedo-randomness (an
|
|
even distribution of return values across the spectrum of available
|
|
numbers) while others will give very bad numbers.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
Frankly I think all that stuff is two complicated. So I'm
|
|
glad I use Linux where I can just use:
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE><BLOCKQUOTE><CODE>
|
|
dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" "
|
|
</CODE></BLOCKQUOTE></BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
... to get all the randomness I want.
|
|
</BLOCKQUOTE>
|
|
<BLOCKQUOTE>
|
|
So, I hope that's more than you wanted to know about generating
|
|
psuedo-random numbers using the shell.
|
|
</BLOCKQUOTE>
|
|
|
|
<!-- end 4 -->
|
|
<!-- . . . . . . . . . . . . . . . . . . . -->
|
|
<HR WIDTH="40%" ALIGN="center">
|
|
<!-- begin 4 -->
|
|
<H3 align="left"><img src="../../gx/dennis/qbubble.gif"
|
|
height="50" width="60" alt="(?) " border="0"
|
|
>Random Numbers</H3>
|
|
|
|
|
|
<p><strong>From Devil Man on Mon, 12 Jun 2000
|
|
</strong></p>
|
|
<P><STRONG>
|
|
Just a Note see below.
|
|
</STRONG></P>
|
|
<P><STRONG>
|
|
And thanks for all the wonderful info and the quick response...
|
|
</STRONG></P>
|
|
<P><STRONG>
|
|
--- The Linux Gazette Answer Guy <<A HREF="mailto:linux-questions-only@ssc.com"
|
|
>linux-questions-only@ssc.com</A>> wrote:
|
|
</STRONG></P>
|
|
<P><STRONG><FONT COLOR="#000066"><EM><BLOCKQuote>
|
|
Getting Random Values in sh
|
|
</BLOCKQuote></EM></FONT></STRONG></P>
|
|
<P><STRONG><FONT COLOR="#000099"><EM>
|
|
<BR>>Hello answer guy I was wondering and have been unable to find
|
|
<BR>>any info about a shell scripting utility or command that can be
|
|
<BR>>used to generate a random number such as if I wanted to create a
|
|
<BR>>shell script to generate a random number between 1-20 or so. It
|
|
<BR>>dose not have to be a all in one basically how do you generate
|
|
<BR>>random numbers and the command line?
|
|
</EM></FONT></STRONG></P>
|
|
<P><STRONG><FONT COLOR="#000099"><EM>
|
|
<BR>>Thanks randomly speaking
|
|
</EM></FONT></STRONG></P>
|
|
<P><STRONG><FONT COLOR="#000066"><EM>
|
|
Well the easiest way, under bash is to simply use the predefined
|
|
"magic" shell variable: $RANDOM. So the following might work for
|
|
you:
|
|
</EM></FONT></STRONG></P>
|
|
<P><STRONG><FONT COLOR="#000066"><EM><BLOCKQuote>
|
|
RANDOM=$$$(date %+s)
|
|
</BLOCKQuote></EM></FONT></STRONG></P>
|
|
<P><STRONG>
|
|
shouldn't the date command be (date +%s)
|
|
</STRONG></P>
|
|
<BLOCKQUOTE><IMG SRC="../../gx/dennis/bbub.gif" ALT="(!)"
|
|
HEIGHT="28" WIDTH="50" BORDER="0"
|
|
>
|
|
Yep. That was a typo.
|
|
</BLOCKQUOTE>
|
|
|
|
<!-- end 4 -->
|
|
<!--startcut ======================================================= -->
|
|
<P> <hr> </p>
|
|
<H5 align="center"><a href="http://www.linuxgazette.com/copying.html"
|
|
>Copyright ©</a> 2000, James T. Dennis
|
|
<BR>Published in <I>The Linux Gazette</I> Issue 55 July 2000</H5>
|
|
<H6 ALIGN="center">HTML transformation by
|
|
<A HREF="mailto:star@tuxtops.com">Heather Stern</a> of
|
|
Tuxtops, Inc.,
|
|
<A HREF="http://www.tuxtops.com/">http://www.tuxtops.com/</A>
|
|
</H6>
|
|
<P> <hr>
|
|
<!-- begin tagnav ::::::::::::::::::::::::::::::::::::::::::::::::::-->
|
|
<p align="center">
|
|
<table width="100%" border="0"><tr>
|
|
<td align="right" valign="center"
|
|
><IMG ALT="" SRC="../../gx/navbar/left.jpg"
|
|
WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="middle" border="0">
|
|
<A HREF="../lg_answer55.html"
|
|
><IMG SRC="../../gx/dennis/answertoc.jpg" align="middle"
|
|
ALT="[ Answer Guy Current Index ]" border="0"></A></td>
|
|
<td align="center" valign="center"><A HREF="../lg_answer55.html#greeting"><img align="middle"
|
|
src="../../gx/dennis/smily.gif" alt="greetings" border="0"></A>
|
|
<A HREF="1.html">1</A>
|
|
<A HREF="2.html">2</A>
|
|
<A HREF="3.html">3</A>
|
|
<A HREF="4.html">4</A>
|
|
<A HREF="5.html">5</A>
|
|
<A HREF="6.html">6</A>
|
|
<A HREF="7.html">7</A>
|
|
<A HREF="8.html">8</A>
|
|
<A HREF="9.html">9</A>
|
|
<A HREF="10.html">10</A>
|
|
<A HREF="11.html">11</A>
|
|
<A HREF="12.html">12</A>
|
|
<A HREF="13.html">13</A>
|
|
<br>
|
|
<A HREF="14.html">14</A>
|
|
<A HREF="15.html">15</A>
|
|
<A HREF="16.html">16</A>
|
|
<A HREF="17.html">17</A>
|
|
<A HREF="18.html">18</A>
|
|
<A HREF="19.html">19</A>
|
|
<A HREF="20.html">20</A>
|
|
<A HREF="21.html">21</A>
|
|
<A HREF="22.html">22</A>
|
|
</td>
|
|
<td align="left" valign="center"><A HREF="../../tag/kb.html"
|
|
><IMG SRC="../../gx/dennis/answerpast.jpg" align="middle"
|
|
ALT="[ Index of Past Answers ]" border="0"></A>
|
|
<IMG ALT="" SRC="../../gx/navbar/right.jpg" align="middle"
|
|
WIDTH="14" HEIGHT="45" BORDER="0"></td></tr></table>
|
|
</p>
|
|
<!-- end tagnav ::::::::::::::::::::::::::::::::::::::::::::::::::::-->
|
|
<P> <hr>
|
|
<!-- *** BEGIN navbar *** :::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
<p align="center">
|
|
<A HREF="../lg_bytes55.html"><IMG ALT="[ Prev ]"
|
|
SRC="../../gx/navbar/prev.jpg"
|
|
WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<IMG ALT="" SRC="../../gx/navbar/left.jpg"
|
|
WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom" >
|
|
<A HREF="../index.html"><IMG ALT="[ Table of Contents ]"
|
|
SRC="../../gx/navbar/toc.jpg"
|
|
WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A>
|
|
<A HREF="../../index.html"><IMG ALT="[ Front Page ]"
|
|
SRC="../../gx/navbar/frontpage.jpg"
|
|
WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<A HREF="../../faq/index.html"><IMG ALT="[ FAQ ]"
|
|
SRC="../../gx/navbar/faq.jpg"
|
|
WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A>
|
|
<IMG ALT="" SRC="../../gx/navbar/right.jpg"
|
|
WIDTH="15" HEIGHT="45" ALIGN="bottom" >
|
|
<A HREF="../lg_tips55.html"><IMG ALT="[ Next ]"
|
|
SRC="../../gx/navbar/next.jpg"
|
|
WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A>
|
|
<!-- *** END navbar *** :::::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
</p>
|
|
<!-- ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -->
|
|
</BODY></HTML>
|
|
<!--endcut ========================================================= -->
|