← All posts

swapcase with the tr command

A light introduction to the tr command and using character classes.

Want to easily swap the case of some string on the *nix command line? The tr command can do that!

tr introduction

The tr command is “translate characters”

It takes a value from STDIN and applies a given translation to that value and sends the result to STDOUT.

The translation is to arguments: string1 and string2. Every character in string1 will be replaced by the corresponding character from string2. Any character that isn’t in string1 will be sent along as-is.

$ echo abcde | tr a z

echo abcde | tr b +
$ echo crab | tr abc ABC

The above command has abc for the first string and ABC for the second. That means tr will pair them up like so

a => A
b => B
c => C

You can declare character ranges as well. And note the strings don’t have to have to one-to-one match. This declaration will turn the characters a, b, c, d, e into M.

$ echo abcde | tr a-e M

everything to uppercase or lowercase

With that knowledge in mind we can easily switch lowercase to uppercase…for ASCII.

$ echo "HI there" | tr a-z A-Z

Or uppercase to lowercase…for ASCII.

$ echo "HI there" | tr A-Z a-z
hi there

But what if we have more than ASCII?

character classes

echo "hi josé" | tr a-z A-Z

That é is still lowercase and not the É it should be.

The solution? Character classes! They already know everything they need to know about non-ASCII characters.

$ echo "hi josé" | tr '[:lower:]' '[:upper:]'

swapping case

That all works great, but we can do more and tell tr not only switch all lowercase to uppercase but all uppercase to lowercase in one transform!

How? Why we just keep going with our translation declarations.

$ echo "HI there" | tr '[:lower:][:upper:]' '[:upper:][:lower:]'

And, again, because we’re using character classes that means unicode letters work as well.

$ echo "CAfé" | tr "[:lower:][:upper:]" "[:upper:][:lower:]"

what are we even doing?

Why do this at all?

Well, in my case, I wanted a list of lowercase letters, uppercase letters, and numbers. printable-ascii can easily provide me with all the characters, but in ASCII order which has uppercase first.

$ printable-ascii --letters --digits --compact

I could tell printable-ascii more explicitly how to order the output by manually defining the ranges in the order I want

$ printable-ascii --range a-z --range A-Z --digits --compact

But the tr command can easily do that work for me. And since I’m dealing explicitly with ASCII I can simply use a-zA-Z

$ printable-ascii --letters --digits --compact | tr A-Za-z a-zA-Z

A light introduction to the tr command and using character classes.