A Little Devil Called tr

Hans de Vreught

Issue #53, September 1998

Here's a useful command for translating or deleting characters in a file.

The program called tr is not a big program; it is quite small and not extremely powerful. However, if you write scripts, you will treasure it as one of your favorites. It is a typical script program, reading from stdin and writing to stdout; there are no file names to provide as arguments. The main function is translating characters. A second important function is deleting characters. Furthermore, tr is capable of squeezing repeated characters into one, but that particular function is rarely used.

Let us begin with translating characters. The tr command takes the form:

tr

While tr reads its input, it replaces characters appearing in string1 by the corresponding characters in string2. So, the command tr abc def will replace a line like “the quick brown fox quickly jumped over the lazy dog” into “the quifk erown fox quifkly jumped over the ldzy dog”. Well, that doesn't make sense, but it does demonstrate how tr works.

Have you ever wanted to capitalize or de-capitalize a file? To capitalize it, you can use:

tr abcdefghijklmnopqrstuvwxyz \
        ABCDEFGHIJKLMNOPQRSTUVWXYZ

Luckily, we can also use ranges of characters to specify the characters more efficiently:

tr a-z A-Z
Ever had those horrible upper case DOS file names? Here's a Bourne script to take care of them:
for f in *; do
      mv $f `echo $f | tr A-Z a-z`
   done
Many UNIX editors allow some text to be processed by the shell. For example, to replace all upper case characters of the next paragraph with lower case while in vi, type:
!}tr A-Z a-z
As another example, the command:
!jtr a-z A-Z
capitalizes the current and next line (the character after the ! is a movement character).

If you read the International Obfuscated C Code Contest (ftp://ftp.uu.net./pub/ioccc/), you frequently see that part of the hints are coded by a method called rot13. rot13 is a Caesar cypher, i.e., a cypher in which all letters are shifted some number of places. For example, a becomes b, b becomes c, ..., y becomes z, and z becomes a. In rot13 each letter is shifted 13 places. It is a weak cypher, and to decipher it, you can use rot13 again. You can also use tr to read the text in this way:

tr a-zA-Z n-za-mN-ZA-M

Another interesting way to use tr is to change files from Macintosh format to UNIX format. For returns, the Macintosh uses \r while UNIX uses \n. GNU tr allows you to use the C special characters, so type:

tr \r \n
If you don't have GNU's version of tr, you can always use the corresponding octal numbers as shown here:
tr \015 \012
You might wonder what would happen if the second string is shorter than the first string. POSIX says this is not allowed. System V says that only that portion of the first string is used that has a matching character in the second string. BSD and GNU pad the second string with its final character in order to match the length of the first string.

The reason this last method is handy becomes clearer when we take complements into account. Assume you wish to make a list of all words and keywords in your listing. When you use -c, tr complements the first string. In C, all identifiers and keywords consist of a-zA-Z0-9_, so those are the characters we want to keep. Thus, we can do the following:

tr -c a-zA-Z0-9_ \n

If we pipe the tr output through sort -u, we get our desired list. If we follow POSIX, the second string would have to describe 193 newline characters (described as \n*193 or \n*). If we use system V, only the zero byte is translated to a newline, since the complement of a-zA-Z0-9_ starts with the zero byte.

The second important use of tr is to remove characters. For this option, you use the flag -d with one string as an argument. To fix up those nasty MS-DOS text files with a ^M at the end of the line and a trailing ^Z, specify tr in this way:

tr -d \015\032

Many people have written a program in C to do this same operation. Well, a C program isn't necessary—you only need to know the right program, tr, with the right flags. The -d flag isn't used often, but is nice to have when needed. You can combine it with the -c flag to delete everything except characters from the string you supplied as an argument.

Repeated characters can be squeezed into a single one using the -s option with one string as an argument. It can also be used to squeeze white space. To remove empty lines, type:

tr -s \n

The -s option can be used with two strings as arguments. In that case, tr first translates the text as if -s were not given and then tries to squeeze the characters in the second string. For instance, we can squeeze all standard white space to a single space by specifying:

tr -s   \n [ *]
The -d flag can also be used with two strings: the characters in the first string will be removed and the characters in the second string will be squeezed.

tr may not be a great program; however, it gets the job done. It is particularly useful in scripts using pipes and command substitutions (i.e., inside the back quotes). If you use tr often, you'll learn to appreciate its capabilities. Small is beautiful.

Hans de Vreught (J.P.M.deVreught@cs.tudelft.nl) is a computer science researcher at Delft University of Technology. He has been using UNIX since 1982 (Linux since 0.99.13). He likes non-virtual Belgian beer, and he is a real globetrotter, having already traveled twice around the world.