Next up is another parameter expansion we've already briefly seen: case modification. In this instance, case refers to lowercase and uppercase letters.
In the yes-no-optimized.sh script we originally created in Chapter 9, Error Checking and Handling, we had the following instructions:
reader@ubuntu:~/scripts/chapter_09$ cat yes-no-optimized.sh
<SNIPPED>
read -p "Do you like this question? " reply_variable
# See if the user responded positively.
if [[ ${reply_variable,,} = 'y' || ${reply_variable,,} = 'yes' ]]; then
echo "Great, I worked really hard on it!"
exit 0
fi
# Maybe the user responded negatively?
if [[ ${reply_variable^^} = 'N' || ${reply_variable^^} = 'NO' ]]; then
echo "You did not? But I worked so hard on it!"
exit 0
fi
As you might expect, the ,, and ^^ found within the curly braces of the variable are the parameter expansions we're talking about.
The syntax, as found on man bash, is as follows:
${parameter^^pattern}
${parameter,pattern}
${parameter,,pattern}
Case modification. This expansion modifies the case of alphabetic characters in parameter. The pattern is expanded to produce a pattern just as in pathname expansion. Each character in the expanded value of parameter is tested against pattern, and, if it matches the pattern, its case is converted. The pattern should not attempt to match more than one character.
In our first script, we haven't used a pattern. When not using a pattern, it is implied that the pattern is a wildcard (in this case, the ?), which means everything matches.
A quick command-line example of both lowercase and uppercase modification should clear this up. First, let's take a look at how we can uppercase a variable:
reader@ubuntu:~/scripts/chapter_16$ string=yes
reader@ubuntu:~/scripts/chapter_16$ echo ${string}
yes
reader@ubuntu:~/scripts/chapter_16$ echo ${string^}
Yes
reader@ubuntu:~/scripts/chapter_16$ echo ${string^^}
YES
If we use a single caret (^), we can see that the first letter of our variables' value will be uppercased. If we use a double caret, ^^, we now have the full value in uppercase.
In a similar manner, commas do the same thing for lowercasing:
reader@ubuntu:~/scripts/chapter_16$ STRING=YES
reader@ubuntu:~/scripts/chapter_16$ echo ${STRING}
YES
reader@ubuntu:~/scripts/chapter_16$ echo ${STRING,}
yES
reader@ubuntu:~/scripts/chapter_16$ echo ${STRING,,}
yes
Because we can choose to uppercase or lowercase the entire value, we can now much more easily compare user input to a predefined value. Regardless of whether the user inputs YES, Yes, or yes, we can verify all these situations with a single check: ${input,,} == 'yes'.
This gives the user fewer headaches, and a happy user is what we want (remember, you are often the user of your own scripts, and you deserve happiness!).
Now, for the pattern, as the man page specifies it. In our personal experience, we have not had to use this option yet, but it is powerful and flexible, so it never hurts to get a little bit more explanation on this.
Basically, the case modification will only be performed if the pattern matches. It can get a little tricky, but you can see how it works here:
reader@ubuntu:~/scripts/chapter_16$ animal=salamander
reader@ubuntu:~/scripts/chapter_16$ echo ${animal^a}
salamander
reader@ubuntu:~/scripts/chapter_16$ echo ${animal^^a}
sAlAmAnder
reader@ubuntu:~/scripts/chapter_16$ echo ${animal^^ae}
salamander
reader@ubuntu:~/scripts/chapter_16$ echo ${animal^^[ae]}
sAlAmAndEr
The first command we run, ${animal^a}, only uppercases the first letter if it matches the pattern: a. Since the first letter is actually an s, the entire word is printed as lowercase.
For the next command, ${animal^^a}, all matching letters are uppercased. So, all three instances of a in the word salamander are given in uppercase.
On the third command, we try to add an extra letter to the pattern. Since this is not the correct way of doing this, the parameter expansion is (likely) trying to find a single letter to match two letters in the pattern. Spoiler alert: this is very much impossible. As soon as we bring some of our regular expression expertise in the mix, we can do what we want: by using [ae], we're specifying that both a and e are valid targets for the case modification operation.
In the end, the animal returned is now sAlAmAndEr, with all vowels uppercased using a custom pattern in combination with the case modification parameter expansion!
As a little bonus, we'd like to share a case modification that is not even present on the man bash page! It is not that complicated, either. If you replace either , or ^ with a tilde, ~, you will get a case reversal. As you might expect, a single tilde will operate only on the first letter (if it matches the pattern, if specified), while a double tilde will match on all instances of the pattern (or everything, if no pattern is specified and the default ? is used).
Take a look:
reader@ubuntu:~/scripts/chapter_16$ name=Sebastiaan
reader@ubuntu:~/scripts/chapter_16$ echo ${name}
Sebastiaan
reader@ubuntu:~/scripts/chapter_16$ echo ${name~}
sebastiaan
reader@ubuntu:~/scripts/chapter_16$ echo ${name~~}
sEBASTIAAN
reader@ubuntu:~/scripts/chapter_16$ echo ${name~~a}
SebAstiAAn
This should be a sufficient explanation of case modification, as all syntaxes are similar and predictable.
Now that you know how to lowercase, uppercase, and even reverse the case of your variables, you should be able to mutate them in any way you like, especially if you add a pattern into the mix, this parameter expansion provides many possibilities!