Spinning and Text Processing

One facet of shell script programming that most people don't realize is that every loop structure acts as its own subshell, so rather than waste space and time with a temporary file, I'll pipe the output of the fmt|tr sequence directly into a while loop:

fmt -w$bigwidth "$1" | tr '{' '\n' | tr '}' '\n' | \
while read line
  if [ $( echo "$line" | grep -c '|' ) -gt 0 ] ; then
    echo "SPIN THIS: $line"
    echo "$line"
  lines=$(( $lines + 1 ))

See how the fmt line ends with | \, and that feeds directly into the while loop? Very handy structure!

Now I'm going to run this code snippet with the sample input file to see what happens:

$ sh spinner.sh spinme.txt
SPIN THIS: idea|concept|inspiration
is that each time you'd use a
SPIN THIS: word|phrase
you instead list a set of
SPIN THIS: similar words|synonyms|alternative words
and the software automatically picks one
SPIN THIS: randomly|at random

That pesky period on its own line is a glitch that'll need to be fixed later, but the basic structure of the script is sound: you can parse and break down the input file data and identify which new lines are selector lines.

The Spinning Function

Instead of just prepending SPIN THIS: before a line that has choices, that's a perfect place to put in a function call to a separate block of code that does the actual work.

One of the most interesting parts of the function is how it figures out how many options there are in the given string. It's a specific instance of the general question "how many occurrences of X are in string Y?", and it exploits the little known -o flag to grep:

grep -o '|' <<< "$*" | wc -l

Take a deep breath; I can talk you through this one! The <<< notation is a variation on the here document (<<) you've hopefully already seen in scripts. The difference is that the result is fed as a single string on stdin.

The "$*" produces the entire argument as given to the function in the main block of the script; the | is the character being counted, and of course, wc -l produces the number of matching lines (in this case, the number of delimiters in the line).

All that, and it's not quite what I want, because a line like word|phrase has one delimiter, but two choices. Here's how I solve that in this first, skeletal version of the function:

function spinline()
  choices=$(grep -o '|' <<< "$*" | wc -l)
  choices=$(( $choices + 1 ))
  echo $choices options, spinning --- $source

In use:

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

That's it for this month. Next month, I'll finish up the function, including implementing a way to pick one entry randomly from a set of n choices, then output the cleaned up copy, ready to use in whatever program or utility you'd like.


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.