BeastieBoy

FreeBSD, Lisp, Emacs, PostgreSQL & co.

Pump up the volume

Pump up the volume

I had an itch to scratch this morning: I wanted to play a bit with functions in the Korn Shell. After all, it's one of the reasons I decided to switch from the tcsh earlier this year. Then, I realised it's been a million years since I've written any shell, and even more so that I've read some ksh code. So, how does this work again? Below is a simple and quick memento on the very basics of ksh programming.

Declaring a function

This is as easy as:

function M {
    echo "mixer"
}

Alternatively, a function can be declared in a C-style form:

M() {
    echo "mixer"
}

There really is no difference between the two styles in terms of functionalities.

Piping to a function

Piping to a function is as easy as asking the function to read from standard input. This can be done as follows:

function A {
    read stdin
}

Comparing strings

Since ksh is POSIX-compliant, it implements the complete set of regex matching plumbing (and then some more, as I've discovered). A simple check goes like this:

function R  {
    read stdin
    pattern='.*:$'
    vols=""
	 
    if [[ $stdin =~ $pattern ]]; then
      # ...
}

Playing with numbers

This is another spot where I believe ksh goes beyond what is required by the POSIX standard. You can do any of the four basic operations, as well as more advanced stuff, like bit-shifting or bitwise logic. Anyting between $(( and )) is considered to be an arithmetic expression. Here's an example:

function R {
    # ...
    if [[ $stdin =~ $pattern ]]; then
      volr=$(($RANDOM & 15));
      vols="${stdin}+${volr}";
    else
      voll=$(($RANDOM & 15));
      vols="${stdin} +${voll}:";
    fi
    
    echo $vols
}

The interesting bit is the &, which is the same operator you find in languages like C. Note that the Korn shell also provides an easy way to generate pseudo-random numbers via the usage of the reserved $RANDOM variable.

Capturing the output of a command

Capturing the ouput of a variable is done very intuitively:

function S {
	 read stdin
	 result=$($stdin)
	 echo $result
}

Making it all available

To make your functions available to both your scripts and your interactive sessions, it is generally recommended you either group them in a nice set of files that would contitute a library, or give them each their own file. Place these files somewhere in your home (I would go for ~/functions, to go with ~/bin) and edit both PATH and FPATH, in ~/.kshrc or ~/.profile to include this directory:

# ~/.kshrc
# ...
# ...
PATH=$PATH:~/functions; export PATH
FPATH=$FPATH:~/functions; export PATH

Putting it together

The last bit of advice I would give would be that when you lose patience, you don't lose your sense of humour. For example, if you've been paying attention ot the examples, you have already noticed that the complete set of functions goes like this:

function M {
	 echo "mixer"
}

function A {
	 read stdin
	 echo "$stdin vol"
}

function R {
	 read stdin
	 pattern='.*:$'
	 vols=""
	 
	 if [[ $stdin =~ $pattern ]]; then
	    volr=$(($RANDOM & 15));
	    vols="${stdin}+${volr}";
	 else
	    voll=$(($RANDOM & 15));
	    vols="${stdin} +${voll}:";
	 fi
	 echo $vols
}

function S {
	 read stdin
	 result=$($stdin)
	 echo $result
}

But how is this funny in any way? Simply because I can now do the following in a shell:

$ M|A|R|R|S
Setting the mixer vol from 5:5 to 18:16.
$ 

Yes, I can type M|A|R|R|S and Pump up the volume1. I know.

Footnotes:

1

Well, a friend of mine didn't find this funny at all, but she can get lost on another planet. As I've heard, Mar(r)s needs women anyway.