tr (Translate)
100%

Text-Fu · Lesson 13

tr (Translate)

Learn how to translate, delete, and squeeze character sets in a standard input stream.

The tr command, short for translate, translates, deletes, or squeezes characters read from stdin. It does not accept ordinary input-file operands, so use a pipe or input redirection to provide data.

The basic syntax is:

tr [OPTIONS] SET1 [SET2]

tr works with character sets rather than words or general regular expressions. Use another tool when a transformation depends on a complete word, line structure, or surrounding context.

Translating Characters

With two sets, characters from SET1 map by position to characters in SET2:

$ echo "hello world" | tr a-z A-Z
HELLO WORLD

Here, lowercase range positions map to corresponding uppercase positions. Quote set expressions so the shell passes them unchanged.

You can also translate one character to another:

$ echo "2026-06-23" | tr '-' '/'
2026/06/23
$ echo "abc123" | tr 'abc' 'ABC'
ABC123

Characters not in SET1 pass through unchanged.

What does printf '%s\n' 'abc123' | tr 'abc' 'ABC' print?

Deleting Characters

Use -d with one set to remove every matching character:

$ echo "My address is 123 Main Street" | tr -d '0-9'
My address is  Main Street

Every digit is removed independently; tr is not identifying a complete number token.

Character classes can describe groups defined by the current locale:

$ echo "Hello, world!" | tr -d '[:punct:]'
Hello world

Deleting newlines joins input lines without inserting a replacement separator:

$ printf "one\ntwo\nthree\n" | tr -d '\n'
onetwothree

:::single-choice{#tr-delete-digits} Which command removes every digit from stdin while leaving other characters unchanged?

::option[tr -d '[:digit:]']{#tr-delete-digit-class .correct explanation="The -d option deletes all characters in the digit class from the input stream."} ::option[tr -s '[:digit:]']{#tr-squeeze-digits explanation="The -s option collapses repeated digits but leaves one character from each run."} ::option[tr '[:digit:]']{#tr-one-set-no-delete explanation="Translation normally needs a second set. A set alone does not request deletion."} :::

Squeezing Repeated Characters

Use -s SET to replace each run of a listed character with one instance of that character:

$ echo "Hello      World,   how   are   you?" | tr -s ' '
Hello World, how are you?

This set contains an ordinary space, so tabs and newlines are not squeezed by that command.

You can squeeze repeated newlines too:

$ printf "one\n\n\nTwo\n" | tr -s '\n'
one
Two

Which command reduces every run of ordinary spaces in stdin to one space?

Using Character Classes and Complements

Character classes make intent clearer than hand-written ranges in many locales. Common classes include:

  • [:lower:]: Lowercase letters.
  • [:upper:]: Uppercase letters.
  • [:digit:]: Digits.
  • [:alpha:]: Letters.
  • [:alnum:]: Letters and digits.
  • [:space:]: Whitespace characters.
  • [:punct:]: Punctuation characters.

For example, convert lowercase text to uppercase with character classes:

$ echo "linux journey" | tr '[:lower:]' '[:upper:]'
LINUX JOURNEY

The -c option complements SET1, meaning every character not in the set. Combine it with -d to retain only selected kinds of characters:

$ echo "user@example.com!" | tr -cd '[:alnum:]'
userexamplecom

This also removes the newline because a newline is not alphanumeric. Add or preserve separators deliberately when record boundaries matter.

What does tr -cd '[:alnum:]' do to stdin?

Building Stream Transformations

Several tr processes can be connected when transformations are clearer as separate stages:

$ echo "Hello,,,     world!!!" | tr -d '[:punct:]' | tr -s ' '
Hello world

For simple tab-separated input, translate tab characters into commas:

$ printf "name\tlevel\npete\tbeginner\n" | tr '\t' ','
name,level
pete,beginner

Because tr reads stdin, a file can be provided with <:

$ tr '[:lower:]' '[:upper:]' < names.txt

Redirect stdout to a different file if you need to save the result. Do not redirect back to the input pathname, because the shell would truncate it before tr reads.

:::single-choice{#tr-read-file-input} Which command makes tr read names.txt as stdin and convert lowercase characters to uppercase?

::option[tr names.txt '[:lower:]' '[:upper:]']{#tr-file-operand explanation="tr does not take an ordinary input filename this way; the extra operand makes the syntax invalid."} ::option[tr -d '[:lower:]' < names.txt]{#tr-delete-lowercase explanation="This reads the file correctly but deletes lowercase letters rather than translating them."} ::option[tr '[:lower:]' '[:upper:]' < names.txt]{#tr-input-redirection .correct explanation="The shell opens names.txt on stdin, and tr maps the lowercase class to the uppercase class."} :::

To practice character-level stream transformations, try this hands-on lab:

  1. Linux tr Command: Character Translating - Learn the Linux tr command for character-level transformations in text streams. You'll practice translating characters, deleting specific characters, working with character classes, and squeezing repeated characters.

Lesson complete

You finished tr (Translate)

You can now transform character streams with focused tr operations.

  • Map characters between corresponding sets.

  • Delete selected characters with -d.

  • Squeeze repeated characters with -s.

  • Use locale-aware classes and complements deliberately.

  • Supply input through stdin rather than a filename operand.

Keep your learning progress

Create a free account to save this lesson and continue learning on any device.

Create a free account
Next Lesson
Back to Text-Fu