A Bourne shell irritation: no wildcard matching operator

August 29, 2010

Here is an irritation that gets me every so often: the Bourne shell has no wildcard match operator that you can use in if checks and the like. You can do wildcard matches, but only in case statements.

(Bash has the [[ =~ ]] operator, but it uses regular expressions instead of shell wildcards. I know, I pick nits, but shell wildcards are often simpler and they match what you use in other sh contexts.)

This comes up surprisingly often, at least in the sort of shell scripts that I write. It's not insurmountable but it is inconvenient and it can make my shell scripts read less clearly. Later shells, such as Plan 9's rc, get this right and have built in wildcard matching and non-matching operators, and I have wound up using them relatively frequently.

(Yes, there is a workaround if you are doing this often enough.)

Of course, like a lot of things about the Bourne shell there are historical and philosophical reasons for this. The biggest one is a programming language design issue: you really want your wildcard matching operator to have shell support so that you do not have to keep quoting the wildcards themselves. Philosophically, the only good place to put this in the Bourne shell is as part of explicit shell syntax (ie, in a case statement); inventing a magic operator that didn't do shell wildcard expansion when used as if it was a command would be at least inconsistent.

(Tom Duff was willing to be this magical when creating rc, fortunately. It may be inconsistent but it's very convenient.)

The difficulty is compounded because the natural place to put such an operator is in test, and test started out as an external program, not something built in to the shell. If not expanding wildcards in something that looks like a command is odd in the Bourne shell, doing so for some arguments to an external program is outright serious magic.

PS: expr is not an adequate substitute for various reasons.

Sidebar: the workaround

case conditions will do variable expansion and then, if the variable expands to a wildcard, do wildcard matching on the result. So the simple way around this is to define a function:

match() {
  case "$1" in
    $2) return 0;;
  return 1

Then you can say 'if match $var "*.c"' and the like. If you have to you can even write vaguely crazy things like 'if match $var "*.c" && [ -f $var ];'.

Comments on this page:

From at 2010-09-05 06:20:10:

Bash supports this out of the box, in [[ ]] the =~ matches regexes, but == does what you want, try this:

$ if [[ x == * ]] ; then echo it works; fi

This works only in bash's [[ ]] (after all it's an attempt to fix 'test' annoyances) and is bash-only, but that's still a lot more portable than rc.

Written on 29 August 2010.
« How many uberblock copies ZFS writes on transaction group commits
My avoidance of Python global variables »

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

Last modified: Sun Aug 29 23:20:14 2010
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.