Upper- or lower-casing strings in bash and zsh

String comparison in most programming languages is case-sensitive. That means that the string 'A' and 'a' are considered different. Humans usually don’t think that way, so there is bound to be trouble and confusion.

If you are looking at single letters, the bracket expansion can be quite useful:

case $input in
    [aA])
        # handle 'a'
        ;;   
    [bB])   
        # handle 'b'
        ;;
    [qQ])
        # handle 'q': quit
        exit 0
        ;;
    *)  
        echo "Option is not available. Please try again"
        ;;
esac

However, for longer strings the bracket expansion gets cumbersome. To cover all case combinations of the word cat you need [cC][aA][tT]. For longer, or unknown strings, it is easier to convert a string to uppercase (all capitals) or lower case before comparing.

sh and bash3

Bash3 and sh have no built-in means to convert case of a string, but you can use the tr tool:

name="John Doe"
# sh
echo $(echo "$name" |  tr '[:upper:]' '[:lower:]' )
john doe

# bash3
echo $(tr '[:upper:]' '[:lower:]' <<< "$name")
john doe

Switch the [:upper:] and [:lower:] arguments to convert to upper case.

There are many other tools available that can provide this functionality, such as awk or sed.

Bash 5

Bash 5 has a special parameter expansion for upper- and lowercasing strings:

name="John Doe"
echo ${name,,}
john doe
echo ${name^^}
JOHN DOE

Zsh

In zsh you can use expansion modifiers:

% name="John Doe"
% echo ${name:l}
john doe
% echo ${name:u}
JOHN DOE

You can also use expansion flags:

% name="John Doe"    
% echo ${(L)name}     
john doe
% echo ${(U)name}
JOHN DOE

In zsh you can even declare a variable as inherently lower case or upper case. This will not affect the contents of the variable, but it will automatically be lower- or uppercased on expansion:

% typeset -l name      
% name="John Doe"
% echo $name        
john doe
% typeset -u name      
% echo $name        
JOHN DOE

7 thoughts on “Upper- or lower-casing strings in bash and zsh”

  1. I like your Bash5 suggestion, but it does not work for me:
    $ name=”John Doe”
    $ echo ${name,,}
    -bash: ${name,,}: bad substitution
    $ bash –version
    GNU bash, version 5.0.16(1)-release (x86_64-apple-darwin18.7.0)

      1. I copy-pasted your example. But just to be safe, I just re-typed it and encounter the same problem. (Indeed, I found your page because I’ve had this problem and have been searching for answers.) Even this simple example always fails. As noted in my original comment, I am using Bash5 on MacOS Mojave,

        [MacOS:~]$ name=”John Doe”
        [MacOS:~]$ echo ${name,,}
        -bash: ${name,,}: bad substitution
        [MacOS:~]$ echo ${name^^}
        -bash: ${name^^}: bad substitution
        [MacOS:~]$

          1. Interesting. Different than what I get when I run bash –version

            [MacOS:~]$ echo $BASH_VERSION
            3.2.57(1)-release
            [MacOS:~]$ bash –version
            GNU bash, version 5.0.16(1)-release (x86_64-apple-darwin18.7.0)
            Copyright (C) 2019 Free Software Foundation, Inc.
            License GPLv3+: GNU GPL version 3 or later

            This is free software; you are free to change and redistribute it.
            There is NO WARRANTY, to the extent permitted by law.
            [MacOS:~]$

          2. Yes, so you installed bash 5. The bash v5 binary is (probably) installed at /usr/local/bin/bash. Since /usr/local/bin is in the default PATH before /bin your shell picks up the new bash before the original /bin/bash. That is why bash —version returns 5

            However, your default shell, the one that starts when you open a Terminal window is still the built-in /bin/bash (v3.2)

            I explain this here: https://scriptingosx.com/2019/02/install-bash-5-on-macos/

          3. Ahah. I see. Frustrating.

            I care more about scripting than my Terminal shell… so I will need to think about whether to use the ‘env’ method or hard-coding the path to bash5 in my shebang.

            Thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.