tr (translate) works on characters, not words or lines. It replaces characters from one set with characters from another, deletes them, or squeezes repeats. It only reads standard input, so you always pipe or redirect into it.

tr SET1 SET2 < input
command | tr [options] SET1 [SET2]

Replace characters

echo "hello world" | tr 'a-z' 'A-Z'       # HELLO WORLD
echo "hello world" | tr ' ' '_'           # hello_world
echo "$PATH" | tr ':' '\n'                # PATH, one entry per line
echo "a,b,c" | tr ',' '\t'                # commas to tabs

Characters are matched by position: the first character of SET1 becomes the first of SET2, and so on. tr 'abc' 'xyz' turns a→x, b→y and c→z. It does not replace the string “abc”.

Character classes work too, and are safer with locales:

tr '[:lower:]' '[:upper:]' < file

Delete characters: -d

tr -d '\r' < windows.txt > unix.txt       # remove Windows CR (fix ^M line endings)
echo "phone: +1 (555) 010-9999" | tr -d -c '0-9\n'    # keep digits only
tr -d '[:space:]' < file                  # remove all whitespace, including newlines
echo "v1.2.3" | tr -d 'v'                 # 1.2.3

Stripping \r fixes shell scripts edited on Windows that fail with bad interpreter: /bin/bash^M. See cat -A to spot those characters.

Squeeze repeats: -s

echo "too    many     spaces" | tr -s ' '      # too many spaces
tr -s '\n' < file                              # remove blank lines
df -h | tr -s ' ' | cut -d' ' -f5              # make aligned output cut-friendly

Squeezing spaces lets cut work on whitespace-aligned command output.

Complement: -c

-c means “every character not in SET1”. It’s usually combined with -d or -s:

tr -cd '[:print:]\n' < file     # strip non-printable characters
tr -cs '[:alnum:]' '\n' < text.txt | sort | uniq -c | sort -rn | head   # word frequency

The second command turns every run of non-alphanumeric characters into a newline, giving one word per line. Then it counts the words. See sort and uniq.

Generate random strings

tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32; echo

This is good for passwords and tokens. openssl rand -base64 24 is an alternative. Note the -dc: delete everything except the allowed characters.

Other handy tricks

echo "Hello" | tr 'A-Za-z' 'N-ZA-Mn-za-m'   # ROT13
tr '\0' '\n' < /proc/1/environ              # make NUL-separated data readable
tr '\n' ',' < list.txt | sed 's/,$/\n/'     # join lines with commas

/proc/<pid>/environ and /proc/<pid>/cmdline separate entries with NUL bytes. tr '\0' '\n' makes them readable. That’s useful for checking which environment variables a running service actually has.

Things tr can’t do

  • Replace whole strings — use sed 's/old/new/g'. See sed.
  • Work on specific fields — use awk or cut.
  • Edit files in place — tr only reads stdin. Write to a new file and move it over the original:
tr -d '\r' < script.sh > script.tmp && mv script.tmp script.sh

Never redirect into the same file you’re reading (tr … < f > f). That empties it.

  • Multibyte characters — GNU tr works byte by byte. It mishandles UTF-8 characters outside ASCII, such as accented letters. Use sed or python3 for those.

Quick reference

Task Command
Uppercase tr '[:lower:]' '[:upper:]'
Replace char tr ' ' '_'
Delete char tr -d '\r'
Keep only digits tr -cd '0-9'
Squeeze spaces tr -s ' '
Split on colon tr ':' '\n'
Random string tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 32

Related: cut and wc.

  • Linux
  • Command Line
  • Text Processing