Comparing Strings in the Shell: Truncate Operator

While creating materials for my bash class I needed to create some globbing exercises for my students.

It occured to me that a string operator and a comparison to the null string can be used to check if something is a substring of another value.

Sure, you can use Substring Expansion to see if the substring is at a known location such as "Does the value of $foo start with 'bar'?", but length and location are often unknown.

$ foo=barfood
$ if [ 'bar' = "${foo:0:3}" ]; then echo $foo; fi
barfood
$

Say, for instance, I wanted to know if it's a word that a Dalek would shriek? Easy to do with regular expressions: grep 'ate$' /usr/share/dict/words.

Since I was building a globbing exercise, regexen would be cheating, so that was out.

Using the Remove matching suffix pattern ( I call it truncate ) operator, one can glob and match to an empty string.

DALEK$ foo=SENATE
DALEK$ if [ '' = "${foo%%*ATE}" ] ; then echo "$foo! $foo!"; fi
SENATE! SENATE!
DALEK$

In that example, ${foo} is the declaration to use the value of the foo variable. Putting a percent symbol, %, after the variable name triggers the truncate operator, which tries to match the included pattern at the end of the variable value.

Note the double percent sign in the example. Default string operator globbing is non-greedy, so we need to double down to get greedy matching.

For example, foo=SENATE; echo ${foo%*} returns SENATE since * means '0 or more' and matching 0 is the least greedy match. Doubling the truncate operator to %% says to greedily match so foo=SENATE; echo ${foo%%*} returns the empty string because * matches the entire string and truncates it.

Back to things Daleks might shriek.

DALEK$ foo="PARLIAMENT"
DALEK$ if [ '' = "${foo%%*ATE}" ] ; then echo "$foo! $foo!"; fi
DALEK$

PARLIAMENT doesn't end with ATE, so that comparison didn't match.

Matching functions for 'anywhere in the string' needs as well.

LIGHTNING$ foo='BICARBONATE'
LIGHTNING$ if [ '' = "${foo%%*CAR*}" ] ; then echo "$foo can be pixarified."; fi
BICARBONATE can be pixarified.
LIGHTNING$ foo='DIHYDROGEN OXIDE'
LIGHTNING$ if [ '' = "${foo%%*CAR*}" ] ; then echo "$foo can be pixarified."; fi
LIGHTNING$

It's also easy to make the search string a variable.

DALEK$ eight=ATE
DALEK$ foo='BICARBONATE'
DALEK$ if [ '' = "${foo%%*${eight}}" ] ; then echo "$foo! $foo!"; fi
BICARBONATE! BICARBONATE!
DALEK$

Glob for the win.

There is one glaring issue, the truncate operator is case sensitive.

See the Pattern Substitution Operator for case insensitive matching.