A Bourne shell irritation: no wildcard matching operator
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;; esac 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 ];
'.
|
|