[Techtalk] real life kewl text-processing

Telsa Gwynne hobbit at aloss.ukuu.org.uk
Sat Jan 10 15:10:35 EST 2004


On Fri, Jan 09, 2004 at 04:03:05PM -0800 or thereabouts, Carla Schroder wrote:
> Howdy gang,
> 
> I'm putting together a sort of 'text and file processing howto', and I'm 
> looking for real-life useful examples. 

Last one from me, I promise. And this was real-life, and it was 
useful to me, even though I look at it now and wonder quite what
it does!

(Short version for people who wonder if it's worth scrolling
down: how to create a 'random' letter in bash when you don't
know there is already a bash builtin for this. With added
obfuscation which all seemed logical at the time.)

I was (am) a huge fan of Nethack. I don't have it installed
currently, and I think this script may not work well on new
Nethacks. It comes from 1998. You could set lots of options
for your character, but some had to be set outside the game.
Things like name and gender. Which I wanted set.  

So I had a script which said something like.

  #!/bin/bash
 
  echo "Which character class? (A)rchaeologist, (B)arbarian, "
  echo "(C)avewoman, (E)lf, (H)ealer, (K)night, (P)riestess, "
  echo "(R)ogue, (S)amurai, (T)ourist, (V)alkyrie, (W)izard? "
  read ANSWER
  case "$ANSWER" in
 
  [aA] ) NETHACKOPTIONS="female,name:Arizona-A,catname:Hawaii,dogname:Texas,noautopickup,nosafe_pet,time,rest_on_space,showexp" ; export NETHACKOPTIONS ; /usr/games/nethack ;;
 
  [bB] ) NETHACKOPTIONS="female,name:Uggi-B,catname:Thuggi,dogname:Huggi,noautopickup,nosafe_pet,time,rest_on_space,showexp" ; export NETHACKOPTIONS ; /usr/games/nethack ;;

  # And so on through the list...

  esac

And this started up nethack with all the "outside the game
only" options set for that game. And life was good. And then 
for some reason, I decided I wanted the option to pick randomly. 
I know very little about bash, and I had absolutely no idea that 
there was a built-in variable called RANDOM. So I made my own
"random" answer. 

It got a bit out of hand. 

First, I wanted a number between 1 and 12 (I had twelve
of these character class options). Then, the number had
to get converted into a letter, one of those from the 
"echo" choices at the top. 

Second, I needed a source of random numbers. In ASCII.
I couldn't understand how /dev/(u)random were supposed
to be used. So it had to be ASCII because then I could
use it. I don't know how I made the connection but the
"obvious" to me source of constantly changing numbers
on the filesystem was, um, /proc/uptime.

/proc/uptime has two numbers like this in it:
  $ cat /proc/uptime
  3180456.63 2997997.22

And if you run /proc/uptime three times in a row, I
guarantee you'll get three very different sets of 
numbers. I don't know how random they are, but they
were always different, and they were in ASCII. 

I am sure some mathematician or OS type will tell me
that this is not random at all, but I really didn't
care. I wanted something that just picked one from a
list and saved me the choice. Whether it's random,
pseudo-random, predictable, or what, I don't care :) 

Good enough. So I could get a number from it. And I wanted
something between one and twelve. So I could use dc's 
"remainder" (it has a proper name but I forget: you get
it with "%", anyway) to get a number between... oh dear. 
Couldn't be between 1 and 12 in normal numbers. But what 
about hex? An answer in hex would mean "tr" would do the 
number->letter conversion; but then I'd have four left
over. Of course! You can tell dc to work in whatever base 
you like. Hello, base-12...

There were all kinds of other problems, and summoning the
local hacker to find out why it didn't work with a backtick
but did with brackets (or whatever, this is a long time
ago now!) and retreating to the man page for bash and 
_still_ not noticing that RANDOM existed... 

But I got there in the end. 

And now for the "real-life useful example of text and file
processing" (honestly, Carla, it is!) that is still there 
in that file. 

Right up immediately after the "read ANSWER", I put this.

  case "$ANSWER" in 
  \? ) X=$(cat /proc/uptime |( read A B; echo $A |sed -e "s/\.//"));
       ANSWER=$( echo "16o $X 12 % p q" |dc | tr "0123456789AB" "abcehkprstvw");
       echo $ANSWER ;;
  esac 

..which took in /proc/uptime, and turned that "3180456.63 
2997997.22" rubbish into one of twelve letters. And that of 
course fell through the rest of the script into whichever 
of the sets of options was appropriate. 

And to this day, I don't know whether to be proud or 
embarrassed. Particularly of the "what's constantly
changing? /proc/uptime". Sometimes I think that was 
really quite clever, but mostly I just cringe. 

I _really_ should have looked harder for the builtin... 

Telsa



More information about the Techtalk mailing list