Text processing
tr
Translate, squeeze or delete characters from standard input; for case changes, line endings and stripping characters.
tr [OPTION...] SET1 [SET2]
tr works on single characters, not words or patterns. With two sets it translates: each
character of SET1 is replaced by the character at the same position in SET2, so tr abc xyz
turns a into x, b into y and c into z. With -d it deletes every character in
SET1, and with -s it squeezes runs of the same character down to one.
Sets can be written as ranges (a-z), escapes (\n, \t, \r) and classes ([:digit:],
[:space:], [:upper:]). -c takes the complement of SET1, "every character not in this
set", which is how you keep only certain characters.
Two things surprise people. tr reads only standard input; it takes no file name, so you write
tr ... < file. And GNU tr works on bytes, so it cannot change the case of accented or other
non-ASCII letters. For replacing strings or patterns, use sed.
Examples
Convert to upper case
tr a-z A-Z < todo.txt
BUY MILK FIX LOGIN BUG WRITE TESTS FIX LOGOUT BUG DEPLOY V2.4
Each lower-case ASCII letter is mapped to the upper-case letter at the same position in the second range.
Use character classes
echo 'Mixed Case Text' | tr '[:upper:]' '[:lower:]'
mixed case text
[:upper:] and [:lower:] name the letter classes; they are clearer than ranges and must be quoted so the shell leaves the brackets alone.
Remove Windows carriage returns
tr -d '\r' < data/windows.txt | od -c
0000000 l i n e o n e \n l i n e t w 0000020 o \n 0000022
-d '\r' deletes every carriage return, converting CRLF line endings to Unix ones. od -c
shows that only \n is left at each line end.
Squeeze repeated spaces
echo 'alpha beta gamma' | tr -s ' '
alpha beta gamma
-s ' ' collapses each run of spaces into one, which makes the text safe to split with cut -d ' '.
Remove blank lines
tr -s '\n' < data/settings.conf
# demo service settings host = 0.0.0.0 port = 8080 # logging log_level = info log_file = /var/log/demo/app.log # max_connections = 100 timeout = 30
Squeezing newlines turns \n\n into \n, which deletes the empty line in the middle of the
file (a blank line at the very start would survive, since it is a single newline).
One PATH entry per line
echo "$PATH" | tr : '\n'
/usr/local/bin /usr/bin /bin
Translating every : into a newline lists each directory of $PATH on its own line.
Change a delimiter
tr , '\t' < data/sales.csv | head -n 3
date region product units price 2026-03-01 north widget 12 4.50 2026-03-01 south gadget 3 19.99
Every comma becomes a tab. This is only safe when no field contains a comma of its own.
Keep only certain characters
echo 'Phone: (555) 123-4567' | tr -cd '0-9'; echo
5551234567
-c complements the set, so -cd '0-9' deletes everything that is not a digit, including
the newline; the trailing echo adds it back.
Replace everything outside a set
echo 'My File (final).txt' | tr -c 'A-Za-z0-9.\n' _
My_File__final_.txt
Every character that is not a letter, digit, dot or newline becomes _, a quick way to make
a safe file name. The newline is in the set so the line ending survives.
ROT13 in one line
echo 'Hello, World' | tr 'A-Za-z' 'N-ZA-Mn-za-m'
Uryyb, Jbeyq
Each letter is mapped 13 places along the alphabet; running the same command again restores the original.
When SET2 is shorter than SET1
echo abcdef | tr abcdef xy
echo abcdef | tr -t abcdef xy
xyyyyy xycdef
GNU tr pads a short SET2 by repeating its last character, so c to f all become y.
With -t SET1 is truncated to SET2's length instead, and only a and b change.
Random characters from /dev/urandom
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16; echo
33opkcQU27VZE1DL
tr deletes every byte that is not a letter or digit, and head stops after 16 of the
survivors. The result is different on every run.
Trap: non-ASCII letters are not converted
echo 'café naïve' | tr '[:lower:]' '[:upper:]'
CAFé NAïVE
GNU tr handles bytes, so the two-byte letters é and ï pass through unchanged:
CAFé NAïVE. Use sed 's/.*/\U&/' or awk '{print toupper($0)}' in a UTF-8 locale instead.
Trap: unquoted brackets are shell globs
touch a
echo banana | tr [a-z] [A-Z]
b[n[n[
Unquoted, [a-z] is a glob, and the file a matches it, so the shell runs tr a '[A-Z]'.
a is mapped to [ and the rest of the set is ignored: b[n[n[. Always quote tr sets.
Error: tr does not take a file name
tr a-z A-Z todo.txt
tr: extra operand ‘todo.txt’ Try 'tr --help' for more information.
tr only reads standard input, so todo.txt is an extra operand. Write tr a-z A-Z < todo.txt.
Common pitfalls
Quote the sets:
tr [a-z] [A-Z]lets the shell expand[a-z]as a glob if a one-letter file name exists in the current directory.trtakes no file arguments. Usetr ... < fileor a pipe.trreplaces characters, not strings:tr 'cat' 'dog'mapsc→d,a→o,t→geverywhere. Usesed 's/cat/dog/g'for strings.GNU
tris byte-based: it does not change the case ofé,ßor other multi-byte characters, and a set containing them can corrupt UTF-8 text.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
When SET2 is shorter than SET1, GNU pads it with its last character; POSIX leaves this unspecified and other implementations may differ. Make the sets the same length to be portable.
BSD/macOS
tris multibyte-aware in UTF-8 locales, sotr '[:lower:]' '[:upper:]'does convertéthere; GNUtrdoes not.
Options worth knowing
A selection, described in our own words. For the full list, see tr(1) or tr --help.
| Option | What it does |
|---|---|
-d, --delete | Delete the characters in SET1 instead of translating. |
-s, --squeeze-repeats | Replace runs of a repeated character from the last set with one occurrence. |
-c, --complement | Use every character not in SET1. |
-C | Same as -c. |
-t, --truncate-set1 | Shorten SET1 to the length of SET2 before translating. |
Related
sedEdit a stream of text with a script of commands, most often to substitute text or delete lines.cutPrint selected fields, characters or bytes from each line, such as columns of a CSV or TSV file.revReverse the characters of every line.iconvConvert text from one character encoding to another, such as Latin-1 or UTF-16 to UTF-8.- QuotingSingle quotes, double quotes, backslashes and $'...', what each protects, and the unquoted-variable bugs they prevent, each shown failing and then fixed.