Matching words against a list in the Bourne Shell

November 12, 2007

One of the interesting challenges in writing shell scripts is figuring out how to do as much as is practical with shell builtins and primitives. Since Bourne shell builtins are such a limited and constrained environment, the results can be somewhat perverse and peculiar and thus neat and amusing.

(If you can find an efficient way of doing things, using builtins is much faster than having to resort to external programs, especially when the system is loaded.)

My challenge today was checking whether a word was in a list of words (words don't have spaces), in a Bourne shell dialect that would work as far back as Solaris 2.5. What I came up with is:

case "$wlist" in
    $w|"$w "*|*" $w "*|*" $w") echo yes;;
    *) echo no;;
esac

(Where $w is the word and $wlist is the space-separated list of words to match against. You need the first match condition to deal with the case where $wlist consists only of $w.)

I sometimes feel I have an advantage in this sort of perversity, because I started out writing shell scripts in a version of the Bourne shell that was so old that it didn't even have test (aka [ ... ]) as a builtin. When you don't have a built in test you get a lot of experience with abusing case, because it is basically the only builtin testing operation you have.

(To this day I have a tendency to use case for condition tests where I really should use if. Something in the back of my mind is still not really convinced that things like 'if [ "$#" -eq 0 ]' don't require an external program.)


Comments on this page:

From 65.172.155.230 at 2007-11-13 12:17:49:

Well it doesn't abuse case, but personally I'd do the above using something like:

 for i in $wlist; do
    if [ "x$i" = "myword" ]; then
     # ...
    fi
 done

...I'd assume my way was faster, but then I usually do :)

From 66.243.153.70 at 2007-11-13 13:35:06:

Or you can use

case " $wlist " in

   *" $w "*) echo yes ;;
   *) echo no ;;

esac

I find it much simpler to read this simpler pattern.

These days I tend to use '(*" $w "*)' i.e. with the leading '(' as it tends to make editors happier.

Icarus Sparry

By cks at 2007-11-14 00:06:27:

Your " $wlist " trick is much better than my brute force approach; thanks, and I've switched my script over to using it. While a for loop would work, I suspect it would be slower and I prefer to do things in one pass if I can.

Written on 12 November 2007.
« Why vfork() got created (part 1)
Why vfork() got created (part 2) »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Nov 12 23:26:09 2007
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.