The tr command in Linux
tr transforms characters as they stream through: map one set to another, delete a set, or squeeze repeats. It has no concept of lines, fields or files (input via stdin only) — pure character surgery, which makes it the quickest tool for case conversion, stripping unwanted characters and preparing text for counting.
How tr works
tr compiles its two sets into a 256-entry translation table at startup, then streams input through it byte by byte — mapping, deleting or squeezing. No lines, no fields, no regex: pure character-level transformation, which is what makes it both extremely fast and pleasantly predictable.
The byte orientation is also its modern caveat: multi-byte UTF-8 characters are seen as individual bytes, so ranges like a-z are safe but accented characters can be mangled. For ASCII log-wrangling — the 95% case — this never bites; for international text, sed or awk with proper locale handling are the safer scalpel.
Syntax
CMD | tr [OPTIONS] SET1 [SET2] Common options
| Option | What it does |
|---|---|
'a-z' 'A-Z' | Map ranges: lowercase to uppercase. |
-d SET | Delete every character in the set. |
-s SET | Squeeze runs of a character to one. |
-c SET | Complement: everything NOT in the set. |
'[:alnum:]' etc. | POSIX classes: alpha, digit, space, punct… |
How to use tr: examples
$ echo 'Hello World' | tr 'a-z' 'A-Z' HELLO WORLD.
$ tr -d '\r' < windows.txt > unix.txt Strip carriage returns — fix Windows line endings in one pass.
$ tr -s ' ' < messy.txt Collapse runs of spaces to single spaces.
$ cat essay.txt | tr -cs '[:alpha:]' '\n' | sort | uniq -c | sort -nr | head The classic word-frequency pipeline: every non-letter becomes a newline, then count.
Real-world use cases for tr
Sanitizing generated names
Turning titles into filenames: echo "$title" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' produces clean-slug-text from anything. Two tr stages replace a regex library for the everyday case.
Fixing cross-platform files
A CSV from Windows breaks your parser: tr -d '\r' < in.csv > out.csv strips the carriage returns in one streaming pass, gigabytes included. The oldest interoperability fix still earning its keep daily.
Pro tips and common mistakes
- tr reads stdin only — always < file or a pipe, never a filename argument.
- POSIX classes ([:alnum:], [:space:]) beat hand-written ranges for anything beyond a-z.
- Single characters → tr; strings and patterns → sed. Choosing correctly keeps both simple.
Frequently asked questions about tr
Why does tr file.txt fail?
tr reads only stdin: tr … < file.txt. It is a pure filter by design.
tr or sed for replacements?
tr maps single characters (fast, no regex); sed replaces strings/patterns. "Change every ; to ," is tr; "change http to https" is sed.
Related commands
Stream-edit text: substitute, delete lines, and edit files in place with -i.
uniqDeduplicate adjacent lines and count occurrences — sort's inseparable partner.