Learn › Linux Foundations › Text Processing
tr - a hands-on Linux lab on a real virtual machine.
Translate, delete, and squeeze characters with tr: map a-z to A-Z, strip a set with -d, collapse repeats with -s, and keep only what you name with -c. Learn the one rule everyone trips on, that tr reads standard input and never a filename, all in the practice terminal.
Here is a small puzzle. You type a lowercase word into the terminal, hand it to one command, and it comes back in capitals. No editor, no find-and-replace, no retyping. The word hello goes in and HELLO comes out.
The command that does this is tr. By the end of this lesson you will use it to change letters, strip out characters you do not want, and flatten messy spacing, all in a single short line. This lesson is all of tr, and nothing else.
The black boxes in this lesson are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. tr is deterministic here: because every input is a short word you can see typed on the same line, the output shown is exactly what any Linux machine prints. At the end of the module one real machine boots just for you, and you run the full mission there.
tr is short for translate. It reads text one character at a time and swaps characters for other characters. It does not understand words or lines; it works letter by letter. That is the whole idea: a character in, a character out.
There is one rule about tr that trips up almost everyone, so learn it before anything else. tr does NOT take a filename. It reads only from standard input, which is the stream of text you feed a command. You feed tr by piping another command's output into it with the | symbol, called a pipe. echo hello prints the word hello, and the pipe hands that word straight to tr.
The simplest job is upcasing. You give tr two sets of characters: the ones to look for, then the ones to replace them with. Position by position, a becomes A, b becomes B, and so on. The shorthand a-z means all lowercase letters and A-Z means all uppercase. Run this now:
echo hello | tr a-z A-Z
prompt: student@linuxcamp:~$ answer: echo hello | tr a-z A-Z output: HELLO hint: Type echo hello, a space, the pipe symbol, then tr a-z A-Z: echo hello | tr a-z A-Z
The word came back in capitals. echo hello produced the text hello, the pipe | carried it into tr, and tr walked through it swapping each lowercase letter for the uppercase letter in the same position of the second set. a-z is the first set (what to find), A-Z is the second set (what to put in its place). That two-set shape, find-set then replace-set, is the heart of the command.
Sometimes you do not want to swap a character, you want it gone. The -d flag stands for delete. With -d you give tr just ONE set: the characters to remove. Everything in that set is dropped, and everything else passes through untouched.
A classic use is stripping spaces out of a string. Feed tr -d a set containing a single space, and every space vanishes:
echo "hello world" | tr -d " "
prompt: student@linuxcamp:~$ answer: echo "hello world" | tr -d " " output: helloworld hint: Add -d and one set with a space in quotes: echo "hello world" | tr -d " "
The two words closed up into one: helloworld. With -d there is no second set, because you are not replacing, you are deleting. The set " " held one character, a space, so tr removed every space and left the letters alone. Swap the set for -d 0-9 and it would strip every digit instead. -d takes one set and erases whatever is in it.
The next flag cleans up messy repetition. -s stands for squeeze. When a character in the set you give it appears several times in a row, -s collapses that run down to a single copy. One set, and it only touches back-to-back repeats.
Picture a line where a comma got typed twice by accident: a,b,,c. You want each field separated by exactly one comma. Squeeze the commas:
echo "a,b,,c" | tr -s "," ","
prompt: student@linuxcamp:~$ answer: echo "a,b,,c" | tr -s "," "," output: a,b,c hint: Use -s with the comma as both sets: echo "a,b,,c" | tr -s "," ","
The doubled ,, became a single ,, so a,b,,c came out as a,b,c. Read the command right to left in your head: -s "," says squeeze runs of commas, and the second "," says the character to keep is a comma. A single comma was already single, so it passed through; only the doubled pair got collapsed. -s is how you tidy runaway spacing or repeated separators down to one clean copy.
Back to the two-set translate form, but with a twist that is genuinely useful. You can turn a single-line list into one item per line by translating the separator into a newline. A newline is the invisible character that ends a line, and in a tr set you write it as \n.
Take a comma-separated list, a,b,c, and translate every comma into a newline so each letter lands on its own line:
echo "a,b,c" | tr "," "\n"
prompt: student@linuxcamp:~$ answer: echo "a,b,c" | tr "," "\n" output: a b c hint: Translate comma to newline, written as backslash n: echo "a,b,c" | tr "," "\n"
One line became three. The first set was "," (find the commas) and the second set was "\n" (replace each with a newline). Every comma turned into a line break, so the three letters stacked up as a, b, c. This is a common move in real work: many tools want one item per line, and tr "," "\n" is the quickest way to get there from a comma list.
One more flag rounds out the set: -c, short for complement. It flips the meaning of your first set. Instead of acting on the characters you listed, tr acts on every character you did NOT list. Paired with -d, it becomes a keep filter: delete everything except the set.
Say a string is a jumble of letters and digits, abc123, and you want only the digits kept. The set 0-9 means all digits, and -cd reads as "delete the complement of the digits", which leaves the digits behind:
echo "abc123" | tr -cd "0-9"
prompt: student@linuxcamp:~$ answer: echo "abc123" | tr -cd "0-9" output: 123student@linuxcamp:~$ hint: Combine -c and -d with the digit range: echo "abc123" | tr -cd "0-9"
Look closely at that output: 123 is immediately followed by the next shell prompt on the SAME line. That is not a mistake. tr deleted the letters AND the trailing newline that echo normally adds, because the newline is not a digit and so it fell outside the kept set 0-9. When tr output ends without a newline, the prompt just carries on from where the text stopped. Seeing the prompt hug your output is a normal sign that the last newline was stripped, not a sign anything broke.
Only the digits survived: 123. -c complemented the set 0-9, turning "the digits" into "everything that is not a digit", and -d deleted exactly that. The letters a, b, c were not digits, so they went; the digits stayed. -cd together is the standard way to say "keep only these characters and throw away the rest".
This is the mistake every tr user makes once. Because so many commands take a filename, people write tr a-z A-Z names.txt and expect tr to read the file. It does not. tr reads standard input only, so it treats names.txt as a SET of characters, not a file to open, and it waits on input that never comes or produces nothing useful.
The fix is to feed the file IN, either with a pipe from cat or with the input redirect <, which points a file into a command's standard input. Both of these read the file correctly:
cat names.txt | tr a-z A-Z
tr a-z A-Z < names.txt
Say the trap out loud so it sticks: tr takes no filename. If you want tr to work on a file, you pipe the file into it with cat file | tr ..., or you redirect it in with tr ... < file. Writing the filename as a plain argument, like tr a-z A-Z file.txt, does NOT read the file; tr reads only what arrives on standard input.
Scaffolding off. No command is printed this time. You have every piece you need.
You want to take the word signal and print it in all capitals, SIGNAL. Remember tr cannot read a word on its own. You have to feed the word in first with echo and a pipe, then give tr the two sets that map lowercase letters to uppercase letters.
prompt: student@linuxcamp:~$ answer: echo signal | tr a-z A-Z output: SIGNAL hint: Echo the word, pipe it in, then translate the lowercase range to the uppercase range. Think echo signal, a pipe, then tr a-z A-Z.
SIGNAL, from memory. You echoed the word, piped it into tr, and mapped a-z onto A-Z so every lowercase letter came back uppercase. That is the whole skill: pick your two sets, feed the text in through a pipe, and let tr swap character for character. It is exactly the move the real machine will ask of you.
You earned this cheat sheet. Every row is a form of tr you have already run:
The one rule to carry away: tr reads standard input, never a filename. You always feed it through a pipe (echo text | tr ... or cat file | tr ...) or with a redirect (tr ... < file). Two sets translate, -d deletes one set, -s squeezes repeats, and -c flips a set to mean everything else.
tr works one character at a time, so it cannot swap whole words or match patterns. The moment you need to replace dog with cat, or match anything cleverer than a fixed list of characters, you have outgrown tr. That job wants sed, which has its own lesson later in this module. Reach for tr when the work is truly per-character: case, deletion, squeezing, or swapping one symbol for another.
The practice terminal has shown you every form of tr: the two-set translate, -d to delete, -s to squeeze, -c to complement, and ranges like a-z. It also drilled the hard rule that tr reads standard input and never a filename. Every one of those you typed yourself.
The Text Processing module ends with one real Linux machine, the text-processing capstone mission, and that is where you translate for real. A VM boots just for you. Its objectives use exactly what you practiced across this module: tr alongside the module's other tools, cut, sort, grep, and sed, each of which has its own lesson. One difference from here: the mission shows no commands. You read the objective, you recall the command, you type it. That recall is what makes it stick.
Finish the other Text Processing lessons, then go translate for real.
Practice tr in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.