Linux Basics: Translate, Delete, and Squeeze Characters with tr
Some text cleanup needs only a character-level transformation, not a full pattern-matching language. GNU tr reads standard input and writes a transformed stream. These examples normalize ASCII letter case, remove carriage returns from a controlled sample, and collapse horizontal spacing without joining lines.
These examples were tested on Linux with GNU Bash 5.2.37 and GNU coreutils 9.7. Use Bash rather than sh. The commands operate only on the small practice inputs shown here.
Prepare a scratch directory
You need GNU tr, Bash, a text editor, and the cat, wc, mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file in this new directory, then run it once in the order shown. Invoking bash explicitly avoids changing executable permissions. No administrator access is needed.
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 11. Normalize ASCII case
Save this as tr-case.sh:
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
printf '%s\n' 'Build Ready: API-7' | tr '[:upper:]' '[:lower:]'Run it:
bash tr-case.shOutput:
build ready: api-7The uppercase class maps to the lowercase class. Spaces, punctuation, digits, and existing lowercase letters in the sample remain unchanged. Single quotes keep the shell from interpreting the character-class syntax before tr sees it.
LC_ALL=C makes this an intentionally ASCII-oriented operation. Do not treat the example as general Unicode case folding; names and text in other writing systems need a Unicode-aware transformation.
2. Remove carriage returns from a known CRLF file
Save this as tr-line-endings.sh:
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
printf 'north\r\nsouth\r\n' > windows.txt
tr -d '\r' < windows.txt > unix.txt
cat -- unix.txt
printf 'Remaining carriage returns: '
tr -cd '\r' < unix.txt | wc -cRun it:
bash tr-line-endings.shOutput:
north
south
Remaining carriage returns: 0The generated input has Windows-style CRLF line endings. Deleting carriage returns leaves the LF separators intact. The final pipeline keeps only carriage returns and counts them, verifying that the result contains zero.
This operation deletes every carriage return, not only those immediately before a newline. That is appropriate for this controlled sample, but inspect real data before using it on mixed line endings or meaningful embedded control characters. Notice that input and output are different files.
3. Collapse blanks but keep line boundaries
Save this as tr-squeeze-blanks.sh:
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
printf 'red green\t\tblue\nblack white\n' | tr -s '[:blank:]' ' 'Run it:
bash tr-squeeze-blanks.shOutput:
red green blue
black whiteThe input includes both repeated spaces and repeated tabs. GNU tr translates each horizontal blank to a space, then -s squeezes repeated spaces. The two original lines remain separate because [:blank:] does not include their newline characters.
Using [:space:] for this translation would also target vertical whitespace. Select the smaller class when line boundaries matter. This example normalizes interior spacing but does not trim a single leading or trailing space.
Keep in mind
The operands describe character arrays, not regular expressions or words. For example, translating a three-letter operand does not mean replacing that three-letter word as a unit. GNU tr primarily supports safe single-byte locales; these examples use ASCII input and the C locale deliberately.
There is no in-place file editing in this workflow. Redirecting a command’s output to its own input pathname can truncate the input before it is read. Write to a distinct file, verify the result, and decide separately whether to replace the original. Keep backups for any irreversible cleanup of real data.
Clean up
In the same terminal, remove only the named practice files, return to your original directory, and remove the now-empty scratch directory:
rm -- windows.txt unix.txt tr-case.sh tr-line-endings.sh tr-squeeze-blanks.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirReferences
Acknowledgements
This post was created with assistance from AI.