ZetCode

Linux tr Command

last modified October 23, 2026

The tr (translate) command in Linux replaces, deletes, or squeezes characters. It reads standard input, transforms it character by character according to one or two sets of characters, and writes the result to standard output.

The syntax is tr [options] SET1 [SET2]. tr takes no file names; input comes from a pipe or a redirection with <. Sets can contain ranges such as a-z, escapes such as \n and \t, and character classes such as [:digit:]. Quote the sets so that the shell does not interpret them. The examples use GNU tr from coreutils.

Changing case

Translating the range of lowercase letters to uppercase letters converts text to uppercase.

tr a-z A-Z < text.txt
tr '[:upper:]' '[:lower:]' < text.txt

Each character of the first set is replaced with the character at the same position in the second set. The character classes [:upper:] and [:lower:] express the same intent more clearly.

$ tr a-z A-Z < text.txt
THE QUICK BROWN FOX.
THE LAZY DOG!
$ tr '[:upper:]' '[:lower:]' < text.txt
the quick brown fox.
the lazy dog!

Translating characters

The two sets map characters one to one.

echo 'hello world' | tr 'lo' 'xy'
echo 'hello world' | tr 'a-y' 'b-z'
echo 'hello world' | tr 'a-z' 'x'

In the first command, l becomes x and o becomes y. Note that tr works with individual characters, not strings: it cannot replace the word lo with xy; use sed for that. The second command shifts each letter by one. If the second set is shorter, GNU tr repeats its last character, so in the third command all letters become x.

$ echo 'hello world' | tr 'lo' 'xy'
hexxy wyrxd
$ echo 'hello world' | tr 'a-y' 'b-z'
ifmmp xpsme
$ echo 'hello world' | tr 'a-z' 'x'
xxxxx xxxxx

Deleting characters

The -d option deletes all characters from the set.

echo 'Phone: +421 (905) 123-456' | tr -d '()+ -'
echo 'Order 66 shipped 2026' | tr -d '[:digit:]'

The first command removes parentheses, the plus sign, spaces, and dashes. The dash is placed last in the set so that it is not taken for a range. The second command removes all digits.

$ echo 'Phone: +421 (905) 123-456' | tr -d '()+ -'
Phone:421905123456
$ echo 'Order 66 shipped 2026' | tr -d '[:digit:]'
Order  shipped 

Converting Windows line endings

Text files created on Windows end their lines with a carriage return and a line feed (CRLF). Deleting the carriage returns converts them to Unix line endings.

tr -d '\r' < dos.txt > unix.txt

The file command reports the line terminators, and od -c shows the \r characters before each \n. After the conversion, they are gone. This fixes scripts that fail with errors such as $'\r': command not found.

$ file dos.txt
dos.txt: ASCII text, with CRLF line terminators
$ tr -d '\r' < dos.txt > unix.txt
$ file unix.txt
unix.txt: ASCII text
$ od -c dos.txt | head -n 2
0000000   l   i   n   e       o   n   e  \r  \n   l   i   n   e       t
0000020   w   o  \r  \n
$ od -c unix.txt | head -n 2
0000000   l   i   n   e       o   n   e  \n   l   i   n   e       t   w
0000020   o  \n

Squeezing repeated characters

The -s option replaces each run of a repeated character from the set with a single occurrence.

echo 'too    many     spaces' | tr -s ' '
echo 'aaabbbccc' | tr -s 'a-c'
printf 'one\n\n\ntwo\n' | tr -s '\n'

Squeezing spaces normalizes the spacing between words, which is useful before splitting text with cut. Squeezing newlines removes empty lines.

$ echo 'too    many     spaces' | tr -s ' '
too many spaces
$ echo 'aaabbbccc' | tr -s 'a-c'
abc
$ printf 'one\n\n\ntwo\n' | tr -s '\n'
one
two

Keeping only selected characters

The -c option uses the complement of the first set, that is, all characters not in it. Combined with -d, it keeps only the listed characters.

echo 'User #42: jano@example.com!' | tr -cd '[:alnum:]@.\n'

All characters except letters, digits, the at sign, the dot, and the newline are deleted. We keep the newline in the set; otherwise the trailing newline would be removed as well.

$ echo 'User #42: jano@example.com!' | tr -cd '[:alnum:]@.\n'
User42jano@example.com

Splitting into lines

Translating a separator to a newline puts each item on its own line.

echo "$PATH" | tr ":" "\n"

The directories of the PATH variable are printed one per line, which makes them easy to read or to process further.

$ echo "$PATH" | tr ":" "\n"
/usr/local/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

ROT13

ROT13 is a simple letter substitution that replaces each letter with the one 13 positions later in the alphabet.

echo 'Hello World' | tr 'A-Za-z' 'N-ZA-Mn-za-m'

Since the alphabet has 26 letters, applying ROT13 twice returns the original text. It hides text from casual reading but provides no security.

$ echo 'Hello World' | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Uryyb Jbeyq
$ echo 'Hello World' | tr 'A-Za-z' 'N-ZA-Mn-za-m' | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Hello World

Word frequency

tr is a key part of the classic word frequency pipeline.

tr -cs '[:alpha:]' '\n' < story.txt | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort -rn | head -n 5

The first tr replaces every run of non-letters with a single newline, which puts each word on its own line. The second one converts the words to lowercase. Then sort groups equal words, uniq -c counts them, and the final sort -rn ranks them by frequency.

$ tr -cs '[:alpha:]' '\n' < story.txt | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort -rn | head -n 5
      5 the
      3 falcon
      2 saw
      2 owl
      2 old

Limitations

GNU tr works with bytes, not with multibyte characters, and it does not read files given as arguments.

echo 'čaj' | tr '[:lower:]' '[:upper:]'
echo 'café' | tr 'é' 'e'
tr a-z A-Z text.txt

The letter č is not converted to uppercase. The character é consists of two bytes in UTF-8; both are translated, the second one to the repeated last character of the second set, so we get two letters e. For non-ASCII text, use sed or a scripting language. The last command fails because tr accepts only sets as arguments.

$ echo 'čaj' | tr '[:lower:]' '[:upper:]'
čAJ
$ echo 'café' | tr 'é' 'e'
cafee
$ tr a-z A-Z text.txt
tr: extra operand ‘text.txt’
Try 'tr --help' for more information.

Best practices for tr

Source

GNU tr manual

In this article, we have used the tr command to change case, translate, delete, and squeeze characters, convert line endings, and count word frequencies.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.