← All 149 books tr, one character change per page
One character change per page

tr, one character change per page

Twenty-eight lessons on the tr command, one character-level change or one tr concept per page: the smallest honest stream editor; transliteration as SET1 to SET2 by position; ranges, repeats, star fills and octal escapes; the last character that repeats when SET2 runs short; -d deletion, -s squeezing, -c complement and the newline trap inside it; the locale trap and the LC_ALL=C habit; case conversion, carriage returns, squeezed spaces, keep-only-digits recipes; ROT13 in one line; tr versus sed and awk; why tr never sees a line; the POSIX portability note; and teaching a friend tr in one sitting.


Steve Hodgkiss 7 lessons

One stream, one page, one honest character change. That's the rhythm.

tr, one character change per page

Twenty-eight lessons on the tr command, one character-level change or one tr concept per page: the smallest honest stream editor; transliteration as SET1 to SET2 by position; ranges, repeats, star fills and octal escapes; the last character that repeats when SET2 runs short; -d deletion, -s squeezing, -c complement and the newline trap inside it; the locale trap and the LC_ALL=C habit; case conversion, carriage returns, squeezed spaces, keep-only-digits recipes; ROT13 in one line; tr versus sed and awk; why tr never sees a line; the POSIX portability note; and teaching a friend tr in one sitting.


Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).

Every fact in this book is as the official source states it, fetched and read during this build: the local manual page tr(1) (GNU coreutils 9.4), the definition of truth for this book, and the Wikipedia articles Tr (Unix), Transliteration, Standard streams, Pipeline (Unix), ROT13, Newline, Sed, AWK, Collation, Locale (computer software), POSIX, ASCII, Text processing, Regular expression, Character encoding and Command-line interface. No facts are sourced from Reddit, Stack Overflow, vendor blogs or commercial sites. Every command and output pair was run on this machine read-only (tr only ever reads standard input and writes standard output), each capture recording its locale environment so the pair is reproducible. SAFETY LAW: no file outside this book directory was written by any demonstration. Honest notes: the cached Wikipedia Locale article is a short disambiguation stub, so locale facts cite Locale (computer software) instead; and the first cached batch of demo captures lost their shell quoting in the recorded command strings, so every pair was re-run and re-captured verbatim during this build, with outputs matching the originals in every case. An independent guide, not affiliated with or endorsed by the Free Software Foundation, the IEEE, or the Wikimedia Foundation.

Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.

© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.

Edition 1.0 · stevehodgkiss.net

Contents

Contents


Part 1 · What tr is
When SET2 runs out4
Part 2 · The sets themselves
Classes in brackets5
Part 3 · The three big flags
-s, the squeezer6
Part 4 · The complement surprise
-c, the complement7
Part 5 · Everyday honest uses
Keeping only digits8
Part 6 · tr beside its kin
Character, not line9
Part 7 · The honest craft
Teaching tr10

Per the tr(1) manual page, translation occurs if -d is not given and both STRING1 and STRING2 appear, ARRAY2 is extended to length of ARRAY1 by repeating its last character as necessary, and excess characters of ARRAY2 are ignored. This machine's capture: printf %b 'aabbccdd' | tr abcd xy printed xxyyyyyy, so both c and d mapped to the repeated y.

Character tools companion · No. 01
the last character

When SET2 runs out

The last char repeats to fit

the shorter set stretches$ tr abcd xyabcdxyyythe last y repeatsaabbccddxxyyyyyya to x, b to y, and bothc and d fall onto therepeated last characterARRAY2 stretches by repeating its last character; a longer ARRAY2 loses its tail

The sets need not match in length. The rule, straight from tr(1): ARRAY2 is extended by repeating its last character until it is as long as ARRAY1.

Our capture: tr abcd xy on aabbccdd printed xxyyyyyy. a became x, b became y, and both c and d fell onto the repeated last y.

The mirror rule is quieter: excess characters of ARRAY2 are ignored. A SET2 longer than SET1 simply loses its tail.

ONE HONEST HOUR

Run printf %b 'aabbccdd' | tr abcd xyz and predict the landing before you press.

Per the tr(1) manual page, the interpreted sequences include [:alnum:] for all letters and digits, [:alpha:] for all letters, [:digit:] for all digits, [:lower:] for all lower case letters, [:upper:] for all upper case letters, [:space:] for all horizontal or vertical whitespace and [:cntrl:] for all control characters; character classes expand in unspecified order; and while translating [:lower:] and [:upper:] may be used in pairs to specify case conversion. This machine's captures: printf %b 'posix classes work' | tr '[:lower:]' '[:upper:]' printed POSIX CLASSES WORK, and printf %b 'ab12cd' | tr -d '[:digit:]' printed abcd.

Character tools companion · No. 02
the named sets

Classes in brackets

[:lower:], [:digit:], [:space:]

brackets name whole families$ tr '[:lower:]' '[:upper:]'posix classes workPOSIX CLASSES WORKthe pair the manual itselfblesses for case conversionthe families, per tr(1)[:digit:][:alpha:][:space:][:cntrl:][:lower:][:upper:]one name, an entire membershiplist; order unspecified, page 25classes are sets, not sequences; the lower and upper pair is the blessed exception

The brackets name whole families: [:digit:], [:alpha:], [:space:], [:cntrl:] and the rest, per tr(1). One name, an entire membership list.

Our captures: tr '[:lower:]' '[:upper:]' printed POSIX CLASSES WORK, the pair the manual itself blesses for case conversion; tr -d '[:digit:]' on ab12cd printed abcd.

One honesty note for page 25: classes expand in unspecified order. They are a set, not a sequence.

ONE HONEST HOUR

Sweep one family away: printf %b 'ab12cd' | tr -d '[:digit:]'.

Per the tr(1) manual page, -s, --squeeze-repeats replaces each sequence of a repeated character that is listed in the last specified ARRAY with a single occurrence of that character, and squeezing occurs after translation or deletion. Per the Wikipedia Tr (Unix) article, the s flag causes tr to compress sequences of identical adjacent characters in its output to a single token, and tr -s '\n' replaces sequences of one or more newline characters with a single newline. This machine's captures: printf %b 'too many spaces here' | tr -s ' ' printed too many spaces here, and tr -s '\n' on a, four blank lines, b, four blank lines, c printed a, b and c on single lines.

Character tools companion · No. 03
the iron

-s, the squeezer

Runs of one become one

runs of one become onetoo    many    spacestoo many spaces herethe same flag on blanks:nine lines collapse to three$ tr -s ' '$ tr -s '\n'a b cthe wiki's own example issqueezing replaces each run of a repeated character with one, after translation

-s irons runs flat: each sequence of a repeated character becomes one, per tr(1). The wiki's own example is tr -s '\n', blank runs to single newlines.

Our captures: tr -s ' ' turned too many spaces into too many spaces here, and tr -s '\n' turned nine-line gaps into single breaks, a b c.

The order is law: squeezing happens after translation or deletion, never before.

ONE HONEST HOUR

Iron one of each: tr -s ' ' on a ragged line you paste, then tr -s '\n' on a ragged paragraph.

Per the tr(1) manual page, -c, -C, --complement uses the complement of ARRAY1. Per the Wikipedia Tr (Unix) article, the c flag indicates the complement of the first set of characters, and the invocation tr -cd '[:alnum:]' therefore removes all non-alphanumeric characters. This machine's captures: printf %b 'call 555-867-5309 now' | tr -d -c '0-9\n' printed 5558675309, and printf %b 'call 555 now' | tr -d -c 'a-z\n' printed callnow.

Character tools companion · No. 04
the inside-out set

-c, the complement

Everything except your list

the list turned inside out$ tr -d -c '0-9\n'call 555-867-5309 now5558675309letters, dash, spaces: gone;digits and the newline: keptkeep digits = drop thecomplement of digitstr -cd '[:alnum:]'the wiki's own recipe:removes all non-alphanumerics-c uses the complement of ARRAY1; keep lists become drop lists with one flag

-c turns a list inside out: ARRAY1 becomes everything except what you typed, per tr(1). A keep list becomes a drop list with one flag.

Our captures: tr -d -c '0-9\n' on call 555-867-5309 now printed 5558675309; the letters-only form printed callnow.

The wiki's own recipe is the same move: tr -cd '[:alnum:]' removes all non-alphanumeric characters. Note the \n riding along; the next page explains why it must.

ONE HONEST HOUR

Keep only letters: printf %b 'call 555 now' | tr -d -c 'a-z\n'.

Per the tr(1) manual page, -c uses the complement of ARRAY1 and -d deletes characters in ARRAY1. Per the Wikipedia ASCII article, 95 code points are printable including digits 0 to 9, and ASCII specifies 33 non-printing control codes. This machine's captures: printf %b 'room 12, floor 7, box 3' | tr -cd '0-9\n' printed 1273, and printf %b 'a with two control bytes b c' | tr -d '[:cntrl:]' printed abc with no line break, because the newline is itself a control character.

Character tools companion · No. 05
the recipe

Keeping only digits

Complement delete, digits kept

the classic keep recipe$ tr -cd '0-9\n'room 12, floor 7, box 31273keep digits and the newline,drop everything elsethe hidden lessontr -d '[:cntrl:]'prints abc with no line break:the newline is itself one ofthe 33 control codesevery keep recipe answers first: does the newline ride along or not

The classic recipe is two flags: tr -cd '0-9\n' keeps digits and drops the world. Our capture: room 12, floor 7, box 3 printed 1273.

The family sweep hides a lesson: tr -d '[:cntrl:]' printed abc with no line break, because the newline is itself one of ASCII's 33 control codes, says the Wikipedia ASCII article.

Every keep recipe must answer one question first: does the newline ride along or not. Page 14 taught the crash; this is the repair.

ONE HONEST HOUR

Keep only the year: printf %b 'built 2026 ok' | tr -cd '0-9\n'.

Per the tr(1) manual page, tr translates, squeezes, and/or deletes characters, and the interpreted sequence \n is new line, one character among others. Per the Wikipedia ASCII article, ASCII is a character encoding standard for representing a particular set of 95 printable and 33 control characters, a total of 128 code points, and the control characters that are still commonly used include carriage return, line feed and tab. This machine's capture: tr -s '\n' on a, four blank lines, b, four blank lines, c printed a, b and c, the newline treated as any other repeatable character.

Character tools companion · No. 06
the nature

Character, not line

The newline is just a byte

the line, seen as a byte01013127nulLFCRDEL95 printable, 33 control,128 code points, 0 to 127;the newline is slot 10$ tr -s '\n'abcblank runs collapse likedouble spaces: just bytestr never sees a line as a line: the newline is one byte among 128

tr never sees the line as a line. The newline is one character, code point 10 of ASCII's 128, listed beside carriage return and tab in the Wikipedia ASCII article.

That is why the whole book works: our capture treated \n as any other repeatable character, and blank runs collapsed like double spaces.

Line tools exist, sed and awk among them; tr is beneath them, at the byte floor, and that floor is exactly its power.

ONE HONEST HOUR

Prove it: run tr -s '\n' on a paragraph with triple blank lines and read the count.

Per this book's plan, the closing lesson teaches one friend tr in one sitting: run tr --version, upper one line with tr a-z A-Z, delete digits with tr -d 0-9, squeeze spaces with tr -s ' ', and finish with the ROT13 line tr A-Za-z N-ZA-Mn-za-m. Per the Wikipedia Tr (Unix) article, the original version of tr was written by Douglas McIlroy and introduced in Version 4 Unix. This machine's captures, re-read from this book: tr --version answering tr (GNU coreutils) 9.4, HELLO STREAM EDITOR from tr a-z A-Z, abcdefghi from tr -d 0-9, too many spaces here from tr -s ' ', and gur dhvpx oebja sbk from the ROT13 line.

Character tools companion · No. 07
pass it on

Teaching tr

Five lines, one sitting

the teaching stripversionupperdeletesqueezeROT13they typetr (GNU coreutils) 9.4HELLO STREAM EDITORabcdefghitoo many spaces heregur dhvpx oebja sbkthey type, you readfive lines, one sittingversion, upper, delete, squeeze, ROT13: the whole book in one sitting

The whole book in one sitting with a friend: version, upper, delete, squeeze, ROT13. Five lines, one story: position, not pattern.

Teach the readings as much as the typing: which flag maps, which drops, which irons, and why the newline must be invited along.

The lines they will meet are this book's own captures: tr (GNU coreutils) 9.4, HELLO STREAM EDITOR, abcdefghi, too many spaces here, gur dhvpx oebja sbk. Let them type every one.

ONE HONEST HOUR

Sit with one friend and teach tr in one sitting; they type the five lines, you read positions aloud.

Index

Index


-c, the complement7
-s, the squeezer6
Character, not line9
Classes in brackets5
Keeping only digits8
Teaching tr10
When SET2 runs out4