Finishing Up the Content Spinner

Pick a Card, Any Card

The basic way to work with random numbers in Bash is to use the special $RANDOM variable. Each time it's referenced, it returns a randomly chosen number between 1 and MAXINT (32767). I constrain it to a specific range by using the modulus function, so this will generate a random number between 0 and MAXVALUE:


randomnum=$(( $RANDOM % $MAXVALUE ))

The double-parent notation triggers mathematical evaluation, but you already know that, right?

To make the bottom be the value 1 instead of zero, I just add a bit more math to the equation:


randomnum=$(( $RANDOM % $MAXVALUE + 1 ))

The script already can identify how many choices are in a specific cluster (for example, "{one|two|three}"), and now we have a simple one-liner to help randomly pick one of the values. The challenge, of course, is to pick the actual string value, not just show a number!

I know, I know—work, work, work.

Halfway through the spinline() function (which I'll show in its entirety in just a sec), $choices stores the count of how many options are in the cluster, and $source is the set of choices, minus the open and close curly brackets.

Here's my first attempt at the random word extraction:


pick=$(( $RANDOM % $choices ))
wordpick=$( echo $source | cut -d\| -f$pick )

But, that generates an error message when run. It's not because of a typo, however—it's legit to use cut and specify the pipe symbol as the field delimiter—but because I haven't compensated for the 0..n selection of the random number generator: request field -f0 from cut, and it complains because, well, there is no field zero.

That's easily fixed now that I understand the problem, however, and so here's version two:


pick=$(( $RANDOM % $choices + 1 ))
wordpick=$( echo $source | cut -d\| -f$pick )

Remember that modulus returns 0..(n-1) for its values, so when there are three choices, for example, $RANDOM % 3 returns 0, 1 or 2. Add one to each, and it's back on track with the values 1, 2 and 3.

With a few useful debugging lines, here's the function in its entirety:


function spinline()
{
  source="$*"
  choices=$(grep -o '|' <<< "$*" | wc -l)
  choices=$(( $choices + 1 ))
  echo $choices options, spinning --- $source
  pick=$(( $RANDOM % $choices + 1 ))
  wordpick=$( echo $source | cut -d\| -f$pick )
  echo I pick choice $pick which is $wordpick
}

Yeah, code. Let's see what happens when I run it with the test sentence as input:


$ sh spinner.sh spinme.txt
The
3 options, spinning --- idea|concept|inspiration
I pick choice 2 which is concept
is that each time you'd use a
2 options, spinning --- word|phrase
I pick choice 1 which is word
you instead list a set of
3 options, spinning --- similar words|synonyms|alternative words
I pick choice 2 which is synonyms
and the software automatically picks one
2 options, spinning --- randomly|at random
I pick choice 2 which is at random
and is done.

It's close, actually—really close!

______________________

Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at www.DaveTaylorOnline.com.