|

Linux CLI 45 🐧patch and tr commands

echo "[section1] key1=value1 key2=value2" > config_v1.ini
echo "[section1] key1=new_value1 new_key=new_value" > config_v2.ini
diff -u config_v1.ini config_v2.ini > config_patch.diff
patch config_v1.ini < config_patch.diff

echo "hello" | tr 'e' 'E'
echo "hello world" | tr 'lo' 'LO'
echo "hello world" | tr -d 'lo'
echo "HELLO WORLD" | tr 'A-Z' 'a-z'

patch and tr sit at opposite ends of the text-processing spectrum. patch applies a diff file to an original — it’s how software updates and bug fixes are distributed. tr translates or deletes individual characters from a stream — it’s the simplest character-level filter on Linux.

Key point: patch works together with diff. diff produces the description of a change; patch applies it. tr works on single characters only — it can’t match words or patterns. For those, you need sed or awk.


a – patch command

patch is used to apply a diff file to an original source. It is useful for applying bug fixes and updating software source. The workflow is: compare the old and new versions with diff, save the output to a patch file, then apply that patch to the original.

Syntax:

patch [options] original-file < patch-file

Common options:

OptionPurpose
-bMake a backup file (.orig)
-cInterpret the patch as a context diff
-fForce — assume the patch is not reversed
-RReverse the patch (undo it)
-p NStrip N leading path components
-i FILERead the patch from FILE instead of stdin
-d DIRChange to DIR first
--dry-runShow what would happen without applying

The workflow:

# 1. Create the original file
$ echo "[section1] key1=value1 key2=value2" > config_v1.ini
$ cat config_v1.ini
[section1] key1=value1 key2=value2

# 2. Create the modified version
$ echo "[section1] key1=new_value1 new_key=new_value" > config_v2.ini
$ cat config_v2.ini
[section1] key1=new_value1 new_key=new_value

# 3. Compare and save the difference as a patch
$ diff -u config_v1.ini config_v2.ini > config_patch.diff
$ cat config_patch.diff
--- config_v1.ini	2024-01-15 10:00:00.000000000 +0000
+++ config_v2.ini	2024-01-15 10:05:00.000000000 +0000
@@ -1 +1 @@
-[section1] key1=value1 key2=value2
+[section1] key1=new_value1 new_key=new_value

# 4. Apply the patch to the original
$ patch config_v1.ini < config_patch.diff
patching file config_v1.ini

# 5. Verify — the files now match
$ diff config_v1.ini config_v2.ini
# (no output = identical)

$ cat config_v1.ini
[section1] key1=new_value1 new_key=new_value

How it works:

┌──────────────────────────────────────────────┐
│           The patch workflow                 │
│                                              │
│  config_v1.ini  ─┐                           │
│                  ├──► diff -u ──► .diff      │
│  config_v2.ini  ─┘                           │
│                                              │
│  Then:                                       │
│                                              │
│  config_v1.ini  ──► patch < .diff ──► patched│
│                                              │
│  The patch contains only the CHANGES,        │
│  not the whole file — small and efficient.   │
│                                              │
└──────────────────────────────────────────────┘

More examples:

# Make a backup before patching
$ patch -b config_v1.ini < config_patch.diff
patching file config_v1.ini
# config_v1.ini.orig is created

# Dry run — see what would happen
$ patch --dry-run config_v1.ini < config_patch.diff
checking file config_v1.ini

# Reverse a patch (undo it)
$ patch -R config_v1.ini < config_patch.diff
patching file config_v1.ini

# Use a context diff instead of unified
$ diff -c config_v1.ini config_v2.ini > config_patch.diff
$ patch config_v1.ini < config_patch.diff

# Patch with -p1 (strip one path component)
$ patch -p1 < fix.patch

# Patch all files in a directory
$ cd project/
$ patch -p1 < ../fix.patch

# Patch from a file instead of stdin
$ patch -i config_patch.diff config_v1.ini

# Patch a whole tree
$ patch -p1 -d /path/to/source < fix.patch

# Force apply
$ patch -f config_v1.ini < config_patch.diff

# See what files a patch affects
$ patch --dry-run -p1 < fix.patch

Why -p1 matters:

Patches generated by git diff or diff -u often include a leading path like a/src/file.c and b/src/file.c. The -p1 flag strips the first component (a/ or b/), so the patch applies cleanly to src/file.c:

# Typical git patch header
--- a/src/main.c
+++ b/src/main.c

# Apply with -p1 to strip the "a/" and "b/"
$ patch -p1 < fix.patch

Common patch levels:

FlagStripsExample path becomes
-p0Nothinga/src/main.c
-p11 componentsrc/main.c
-p22 componentsmain.c

Tip: If a patch fails, patch creates a .rej file with the rejected hunks. Inspect it, fix manually, and re-run. Look for .orig (from -b) and .rej files after a failure.

Fixing a failed patch:

$ patch -p1 < fix.patch
patching file src/main.c
Hunk #1 FAILED at 42.
1 out of 1 hunk FAILED -- saving rejects to file src/main.c.rej

# The .rej file contains the hunk that couldn't apply
$ cat src/main.c.rej
--- src/main.c
+++ src/main.c
@@ -42,7 +42,7 @@
-    old_function();
+    new_function();

# Edit the file manually, apply the change, and remove the .rej
$ nano src/main.c
$ rm src/main.c.rej

b – tr command

tr is used to delete or translate characters. It reads from standard input and writes to standard output. It performs character replacements according to a mapping defined by its arguments.

Syntax:

tr [options] SET1 [SET2]
OptionPurpose
-dDelete characters in SET1
-sSqueeze repeated characters into one
-cUse the complement of SET1
-tTruncate SET1 to the length of SET2

How it works: tr maps each character in SET1 to the corresponding character in SET2, position by position. If SET1 is longer than SET2, the last character of SET2 is repeated.

Basic examples:

# Change 'e' to 'E'
$ echo "hello" | tr 'e' 'E'
hEllo

# Change 'l' to 'L' and 'o' to 'O'
$ echo "hello world" | tr 'lo' 'LO'
heLLO wOrLd

# Delete 'l' and 'o'
$ echo "hello world" | tr -d 'lo'
he wrd

# Change all uppercase to lowercase
$ echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world

Ranges and character classes:

# Uppercase to lowercase
$ echo "HELLO" | tr 'A-Z' 'a-z'
hello

# Lowercase to uppercase
$ echo "hello" | tr 'a-z' 'A-Z'
HELLO

# Digits to a placeholder
$ echo "call 555-1234" | tr '0-9' '#'
call ###-####

# ROT13 (classic cipher)
$ echo "hello" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
uryyb

POSIX classes in tr:

ClassMatches
[:alnum:]Letters and digits
[:alpha:]Letters
[:digit:]Digits
[:lower:]Lowercase
[:upper:]Uppercase
[:space:]Whitespace
[:punct:]Punctuation
# Uppercase to lowercase using classes
$ echo "HELLO" | tr '[:upper:]' '[:lower:]'
hello

# Lowercase to uppercase
$ echo "hello" | tr '[:lower:]' '[:upper:]'
HELLO

# Delete all digits
$ echo "abc123def456" | tr -d '[:digit:]'
abcdef

# Delete all punctuation
$ echo "hello, world!" | tr -d '[:punct:]'
hello world

# Replace whitespace with newline
$ echo "a b c d" | tr ' ' '\n'
a
b
c
d

Squeeze mode (-s):

# Collapse multiple spaces into one
$ echo "hello     world" | tr -s ' '
hello world

# Collapse repeated newlines
$ printf "a\n\n\nb\n\nc\n" | tr -s '\n'
a
b
c

# Collapse repeated characters
$ echo "aaabbbccc" | tr -s 'abc'
abc

Complement mode (-c):

# Delete everything EXCEPT digits
$ echo "abc123def456" | tr -cd '[:digit:]'
123456

# Delete everything except letters and newlines
$ echo "hello123world!" | tr -cd '[:alpha:]\n'
helloworld

# Replace everything except digits with a space
$ echo "abc123def" | tr -c '[:digit:]' ' '
   123

Common special characters:

EscapeMeaning
\nNewline
\tTab
\rCarriage return
\\Backslash
\0Null
# Convert newlines to spaces
$ printf "a\nb\nc\n" | tr '\n' ' '
a b c

# Convert spaces to newlines (one word per line)
$ echo "a b c d" | tr ' ' '\n'
a
b
c
d

# Remove carriage returns (Windows line endings)
$ tr -d '\r' < windows.txt > unix.txt

# Convert tabs to spaces
$ tr '\t' ' ' < file.txt

# Delete all non-printable characters
$ tr -cd '[:print:]\n' < file.txt

Important: tr is not a word-replacer. tr 'lo' 'LO' doesn’t replace the word “lo” — it replaces every l with L and every o with O. For word replacement, use sed 's/lo/LO/g'.

tr vs sed:

Aspecttrsed
Works onSingle charactersPatterns/lines
Can delete charsYes (-d)Yes (s///)
Can squeezeYes (-s)With regex
Can translateYesWith y///
Use caseCharacter-level filtersPattern/word replacement

Complete Example Session

# ============================================
# PART 1: CREATE THE ORIGINAL AND NEW FILES
# ============================================

$ echo "[section1] key1=value1 key2=value2" > config_v1.ini
$ cat config_v1.ini
[section1] key1=value1 key2=value2

$ echo "[section1] key1=new_value1 new_key=new_value" > config_v2.ini
$ cat config_v2.ini
[section1] key1=new_value1 new_key=new_value

# ============================================
# PART 2: CREATE THE PATCH
# ============================================

$ diff -u config_v1.ini config_v2.ini > config_patch.diff
$ cat config_patch.diff
--- config_v1.ini	2024-01-15 10:00:00.000000000 +0000
+++ config_v2.ini	2024-01-15 10:05:00.000000000 +0000
@@ -1 +1 @@
-[section1] key1=value1 key2=value2
+[section1] key1=new_value1 new_key=new_value

# ============================================
# PART 3: APPLY THE PATCH
# ============================================

$ patch config_v1.ini < config_patch.diff
patching file config_v1.ini

$ cat config_v1.ini
[section1] key1=new_value1 new_key=new_value

$ diff config_v1.ini config_v2.ini
# (no output = identical)

# ============================================
# PART 4: PATCH WITH BACKUP
# ============================================

$ echo "original" > file.txt
$ echo "modified" > file.new
$ diff -u file.txt file.new > file.patch
$ patch -b file.txt < file.patch
patching file file.txt

$ ls file.txt*
file.txt  file.txt.orig

$ cat file.txt.orig
original

# ============================================
# PART 5: REVERSE A PATCH
# ============================================

$ patch -R file.txt < file.patch
patching file file.txt

$ cat file.txt
original

# ============================================
# PART 6: TR — TRANSLATE
# ============================================

$ echo "hello" | tr 'e' 'E'
hEllo

$ echo "hello world" | tr 'lo' 'LO'
heLLO wOrLd

# ============================================
# PART 7: TR — DELETE
# ============================================

$ echo "hello world" | tr -d 'lo'
he wrd

$ echo "abc123def456" | tr -d '[:digit:]'
abcdef

# ============================================
# PART 8: TR — CASE CONVERSION
# ============================================

$ echo "HELLO WORLD" | tr 'A-Z' 'a-z'
hello world

$ echo "hello world" | tr 'a-z' 'A-Z'
HELLO WORLD

$ echo "HELLO" | tr '[:upper:]' '[:lower:]'
hello

# ============================================
# PART 9: TR — SQUEEZE
# ============================================

$ echo "hello     world" | tr -s ' '
hello world

$ echo "aaabbbccc" | tr -s 'abc'
abc

# ============================================
# PART 10: TR — COMPLEMENT
# ============================================

$ echo "abc123def456" | tr -cd '[:digit:]'
123456

$ echo "hello123world!" | tr -cd '[:alpha:]\n'
helloworld

# ============================================
# PART 11: TR — NEWLINES AND SPACES
# ============================================

# Newline to space
$ printf "a\nb\nc\n" | tr '\n' ' '
a b c

# Space to newline
$ echo "a b c d" | tr ' ' '\n'
a
b
c
d

# Remove carriage returns (Windows → Unix)
$ tr -d '\r' < windows.txt > unix.txt

# ============================================
# PART 12: COMBINING TR WITH OTHER TOOLS
# ============================================

# Word frequency count
$ cat book.txt | tr -s '[:space:]' '\n' | sort | uniq -c | sort -rn | head -10

# Clean a list of emails
$ cat emails.txt | tr 'A-Z' 'a-z' | sort -u

# One word per line
$ echo "the quick brown fox" | tr ' ' '\n'
the
quick
brown
fox

Quick Reference

patch

CommandPurpose
patch FILE < patch.diffApply a patch
patch -b FILE < patch.diffApply with backup
patch -R FILE < patch.diffReverse a patch
patch -p1 < patch.diffStrip one path component
patch -p0 < patch.diffNo stripping
patch -p2 < patch.diffStrip two components
patch --dry-run < patch.diffPreview only
patch -i patch.diff FILERead patch from file
patch -d DIR < patch.diffChange directory first
patch -f FILE < patch.diffForce apply
patch -c FILE < patch.diffContext diff format

tr

CommandPurpose
tr 'a' 'b'Replace a with b
tr 'abc' 'xyz'Map a→x, b→y, c→z
tr 'A-Z' 'a-z'Uppercase to lowercase
tr 'a-z' 'A-Z'Lowercase to uppercase
tr -d 'SET'Delete characters
tr -s 'SET'Squeeze repeats
tr -c 'SET'Complement
tr -cd 'SET'Delete complement
tr '[:upper:]' '[:lower:]'Case using classes
tr '\n' ' 'Newline to space
tr ' ' '\n'Space to newline
tr -d '\r'Remove CR
tr -s '[:space:]'Squeeze whitespace

tr — Character Classes

ClassMatches
[:alnum:]Letters + digits
[:alpha:]Letters
[:digit:]Digits
[:lower:]Lowercase
[:upper:]Uppercase
[:space:]Whitespace
[:punct:]Punctuation
[:print:]Printable
[:cntrl:]Control characters

patch vs tr

Aspectpatchtr
InputDiff file + originalstdin
OutputPatched filestdout
PurposeApply changesTranslate/delete chars
Works onLines/hunksSingle characters
Companiondiffsed, awk

Best Practices

Do This:

# Always use -u with diff when creating patches
diff -u old.txt new.txt > fix.patch    # ✅

# Back up before patching
patch -b file.txt < fix.patch          # ✅

# Dry run first on important patches
patch --dry-run file.txt < fix.patch   # ✅

# Use -p1 for git-generated patches
patch -p1 < fix.patch                  # ✅

# Use -R to undo a patch
patch -R file.txt < fix.patch          # ✅

# Use tr classes for portability
tr '[:upper:]' '[:lower:]'             # ✅

# Use tr -cd to keep only what you want
tr -cd '[:digit:]'                     # ✅

# Use tr -s to squeeze whitespace
tr -s '[:space:]'                      # ✅

# Remove CR from Windows files
tr -d '\r' < win.txt > unix.txt        # ✅

Don’t Do This:

# Don't apply a patch without checking it
patch file.txt < unknown.patch         # ❌ may break things

# Don't forget -p when paths are prefixed
patch < fix.patch                      # ❌ may fail

# Don't patch without a backup on critical files
patch file.txt < fix.patch             # ❌ no undo

# Don't use tr for word replacement
tr 'lo' 'LO'                           # ❌ character-level only

# Don't expect tr to handle regex
tr 'l+' 'L'                            # ❌ + is literal

# Don't use tr on binary files
tr 'a' 'b' < binary                    # ❌ corrupts data

# Don't forget quotes around sets
tr a-z A-Z                             # ⚠️  may shell-expand

# Don't use tr to replace multi-char strings
tr 'hello' 'world'                     # ❌ maps h→w, e→o, l→r...

Common Pitfalls

PitfallProblemSolution
Patch failsWrong -p levelTry -p0, -p1, -p2
.rej filesHunks rejectedApply manually
No backupCan’t undoUse -b
Patch already appliedReversed detectionUse -R or -f
tr word confusionOnly chars replacedUse sed
tr unquotedShell expands rangesQuote: 'a-z'
tr no stdinNothing to translatePipe input
tr with binaryCorrupts dataUse other tools
tr -d over-deletesRemoves wanted charsTest with echo first
tr case classesLocale-dependentSet LC_ALL=C if needed

Real-World Examples

1. Apply a Bug Fix

$ cd project/
$ patch -p1 < /path/to/fix.patch
patching file src/main.c
patching file src/utils.c

2. Create and Apply a Config Patch

$ diff -u config.old config.new > config.patch
$ patch -b config.old < config.patch
patching file config.old

3. Undo a Patch

$ patch -R file.txt < fix.patch
patching file file.txt

4. Dry-Run an Important Patch

$ patch --dry-run -p1 < big-change.patch
checking file src/main.c
checking file src/utils.c

5. Fix Windows Line Endings

$ tr -d '\r' < windows.txt > unix.txt
$ file unix.txt
unix.txt: ASCII text

6. Convert a File to Uppercase

$ tr 'a-z' 'A-Z' < input.txt > output.txt

7. Remove Punctuation from Text

$ echo "Hello, world! How are you?" | tr -d '[:punct:]'
Hello world How are you

8. Keep Only Digits

$ echo "Call 555-1234 or 555-5678" | tr -cd '[:digit:]\n'
555123455556 78

9. Collapse Whitespace

$ echo "too    many     spaces" | tr -s ' '
too many spaces

10. One Word Per Line

$ echo "the quick brown fox" | tr ' ' '\n'
the
quick
brown
fox

11. ROT13 Cipher

$ echo "hello world" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
uryyb jbeyq

12. Remove Non-Printable Characters

$ tr -cd '[:print:]\n' < messy.txt > clean.txt

13. Tabs to Spaces

$ tr '\t' '    ' < input.txt > output.txt

14. Word Frequency Count

$ cat book.txt | tr -s '[:space:]' '\n' | sort | uniq -c | sort -rn | head -10
   5432 the
   3210 and
   2987 of

15. Normalize Emails to Lowercase

$ cat emails.txt | tr 'A-Z' 'a-z' | sort -u
alice@example.com
bob@example.com

16. Patch a Whole Directory Tree

$ patch -p1 -d /opt/app < update.patch
patching file src/main.c
patching file src/utils.c

17. Check What a Patch Would Do

$ patch --dry-run -p1 < update.patch

18. Reverse a Set of Patches

$ for p in *.patch; do
    patch -R -p1 < "$p"
  done

Visual: The patch Workflow

┌──────────────────────────────────────────────┐
│           The patch workflow                 │
│                                              │
│  file_v1  ─┐                                 │
│            ├──► diff -u ──► fix.patch        │
│  file_v2  ─┘                                 │
│                                              │
│  fix.patch contains:                         │
│    --- file_v1                               │
│    +++ file_v2                               │
│    @@ -1 +1 @@                               │
│    -old line                                 │
│    +new line                                 │
│                                              │
│  Then:                                       │
│                                              │
│  file_v1 ──► patch < fix.patch ──► file_v2   │
│                                              │
│  Small file, big effect                      │
│                                              │
└──────────────────────────────────────────────┘

Visual: How tr Works

┌──────────────────────────────────────────────┐
│              tr 'lo' 'LO'                    │
│                                              │
│  Input:   h e l l o   w o r l d              │
│           │ │ │ │ │   │ │ │ │ │              │
│           ▼ ▼ ▼ ▼ ▼   ▼ ▼ ▼ ▼ ▼              │
│  Map:     - - l→L l→L o→O - o→O - l→L -      │
│           │ │ │ │ │   │ │ │ │ │              │
│  Output:  h e L L O   w O r L d              │
│                                              │
│  Character-by-character mapping              │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│              tr -d 'lo'                      │
│                                              │
│  Input:   h e l l o   w o r l d              │
│           │ │ │ │ │   │ │ │ │ │              │
│           ▼ ▼ ▼ ▼ ▼   ▼ ▼ ▼ ▼ ▼              │
│  Delete:  - - X X -   - X - X -              │
│           │ │   │     │   │   │              │
│  Output:  h e     w   r                       │
│           → "he wrd"                         │
│                                              │
│  Deletes every matching character            │
│                                              │
└──────────────────────────────────────────────┘

Summary

CommandPurposeExample
diff -u old new > fix.patchCreate a patchdiff -u a b > p.diff
patch FILE < fix.patchApply a patchpatch config.ini < p.diff
patch -b FILE < fix.patchWith backuppatch -b a.txt < p.diff
patch -R FILE < fix.patchReverse a patchpatch -R a.txt < p.diff
patch -p1 < fix.patchStrip path componentspatch -p1 < p.diff
patch --dry-run < fix.patchPreviewpatch --dry-run < p.diff
patch -i FILE patchRead patch from filepatch -i p.diff a.txt
tr 'e' 'E'Replace a characterecho hello | tr e E
tr 'lo' 'LO'Map multiple charsecho hello | tr lo LO
tr 'A-Z' 'a-z'Change caseecho HELLO | tr A-Z a-z
tr -d 'lo'Delete charactersecho hello | tr -d lo
tr -s ' 'Squeeze repeatsecho "a b" | tr -s ' '
tr -c '[:digit:]' ' 'Complementtr -c 0-9 ' '
tr -cd '[:digit:]'Keep only digitstr -cd 0-9
tr '\n' ' 'Newline to spacetr '\n' ' '
tr ' ' '\n'Space to newlinetr ' ' '\n'
tr -d '\r'Remove CRtr -d '\r'
tr '[:upper:]' '[:lower:]'Case classestr '[:upper:]' '[:lower:]'

Key takeaways:

  • patch applies a diff file to an original — the classic way software updates are distributed
  • The workflow is: diff -u old new > fix.patch then patch old < fix.patch
  • Use -b to back up, -R to reverse, -p1 to strip path components
  • Use --dry-run before applying important patches
  • Failed hunks go to .rej files — apply them manually
  • tr translates or deletes single characters from stdin → stdout
  • Use -d to delete, -s to squeeze, -c for complement
  • Use [:upper:] / [:lower:] for case conversion
  • tr cannot replace words — it’s character-level only; use sed for that
  • Use tr -d '\r' to fix Windows line endings
  • Use tr ' ' '\n' to turn a sentence into one word per line
  • Both tools are pipelines — combine them with sort, uniq, grep, and sed

Remember: patch is how changes travel — small diff files that transform one version into another. Always use diff -u to create patches, always back up before applying, and always dry-run on critical files. tr is the simplest character filter — translate, delete, squeeze. It doesn’t understand words or patterns, only individual characters. When you need word replacement, reach for sed; when you need to map every a to b, tr is the fastest tool for the job.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!