How to Replace Text in a String in Bash
Quick Answer: Replace Text in a Bash String
To replace text in a string, use parameter expansion: ${text/old/new} for the first occurrence, or ${text//old/new} to replace all. It’s built-in, fast, and requires no external commands.
Quick Comparison: Which Method Should You Use?
| Method | Speed | Best For | Complexity |
|---|---|---|---|
| Parameter expansion | Fastest | Simple text, variables | Very simple |
| sed | Very fast | Regex patterns, files | Moderate |
| tr | Fast | Single characters | Simple |
| awk | Fast | Field-based replacement | Moderate |
Bottom line: Use parameter expansion first. If it doesn’t meet your needs, then reach for sed.
Method 1: Parameter Expansion (The Go-To Approach)
Parameter expansion is the fastest method because it uses built-in Bash functionality—no need to spawn external processes or pipe to other commands. When your text is already in a variable, this is your best choice. It’s pure Bash, meaning it works everywhere without installing anything extra.
Replace First Occurrence
To replace just the first instance of something, use a single slash. This is perfect when you have multiple occurrences but only want to fix one:
text="The cat sat on the mat"
result=${text/cat/dog}
echo "$result"
# Output: The dog sat on the mat
Notice how only the first “cat” became “dog”—the second occurrence in “cat” would have stayed as-is if it existed. This is useful when you’re doing replacements where you don’t want to accidentally change everything at once.
Replace All Occurrences
When you need to replace every single instance of a word or phrase, double your slashes. This is the workhorse of string replacement—use this when you want a complete substitution throughout the entire string:
text="cat cat cat"
result=${text//cat/dog}
echo "$result"
# Output: dog dog dog
See the double slash //? That tells Bash “replace ALL occurrences, not just the first one.” This is much faster than looping through or piping to sed when you just need a simple global replacement in a variable that’s already in memory.
When to Use Parameter Expansion
Use parameter expansion when:
- Your text is already in a Bash variable
- You’re doing simple literal text replacement (no patterns needed)
- You care about speed (it’s 5-10x faster than sed for this use case)
- You don’t need case-insensitive matching
- You want the simplest, most readable code
Don’t use it when:
- You need regex patterns or complex matching
- You’re editing files (use sed with
-iinstead) - You need case-insensitive replacement
- The text contains special regex characters that need escaping
Replace at Start of String
Use /# to replace only at the beginning of the string. This is handy when you want to change the prefix but leave identical text elsewhere untouched:
text="hello world hello"
result=${text/#hello/hi}
echo "$result"
# Output: hi world hello
Only the first “hello” (at the start) changed to “hi.” The second “hello” at the end stayed the same. This is exactly what you’d want when processing paths or filenames where the prefix matters.
Replace at End of String
Similarly, use /% to replace only at the end. This is useful for changing file extensions or suffixes:
text="hello world hello"
result=${text/%hello/bye}
echo "$result"
# Output: hello world bye
Notice how only the last “hello” changed? This pattern is perfect for operations like changing file extensions (from .txt to .bak) where you only care about the ending.
Case-Insensitive Replacement (A Common Gotcha)
Here’s where many people get tripped up: parameter expansion is case-sensitive. If you need case-insensitive replacement, you’ll need to use sed or tr instead. This is a hard limitation of the parameter expansion method:
text="HELLO hello HeLLo"
result=${text/hello/hi}
echo "$result"
# Output: HELLO hi HeLLo (only lowercase "hello" matched)
See how “HELLO” and “HeLLo” didn’t change? Only the exact lowercase “hello” matched. If case-insensitivity matters for your use case, skip ahead to the sed section below.
Method 2: Using sed (For Complex Patterns & File Editing)
Use sed when you need regex patterns, case-insensitive matching, or need to edit files directly. Parameter expansion is faster for simple cases, but sed is the workhorse when you need power. The basic syntax is sed 's/old/new/' where s means “substitute.”
Basic sed Replacement
Here’s the simplest sed pattern—it looks cryptic at first, but you’ll use it constantly once it clicks. The three slashes separate the pattern you’re searching for from the replacement:
text="hello world"
echo "$text" | sed 's/hello/hi/'
# Output: hi world
Sed replaces only the first “hello” on each line. If you had multiple occurrences, only the first one gets replaced. This is a gotcha many people miss—you’ll need the g flag to change that behavior.
Replace All Occurrences (The Global Flag)
Add g at the end to replace EVERY occurrence on each line. This is the most common sed pattern you’ll use:
text="cat cat cat"
echo "$text" | sed 's/cat/dog/g'
# Output: dog dog dog
That g is critical—without it, you’d miss the second and third “cat.” The pattern s/old/new/g means “substitute all occurrences of old with new on each line.” Memorize this one.
When to Use sed
Use sed when:
- You need regex patterns or complex matching
- You’re editing files directly (with
-iflag) - You need case-insensitive replacement (add
iflag) - You’re processing streams or large files
- You need backreferences or capture groups
Avoid sed when:
- Doing simple literal replacement on small strings (use parameter expansion instead—it’s faster)
- You’re a beginner (the learning curve is steeper)
Case-Insensitive Replacement
text="HELLO hello HeLLo"
echo "$text" | sed 's/hello/hi/gi'
# Output: hi hi hi
Replace in File In-Place
# Original file
sed -i 's/old_text/new_text/g' filename.txt
# With backup
sed -i.bak 's/old_text/new_text/g' filename.txt
Using Regex Groups and Backreferences
# Swap first and last names
text="John Doe"
echo "$text" | sed 's/\([A-Za-z]*\) \([A-Za-z]*\)/\2, \1/'
# Output: Doe, John
Replace with Special Characters
# When replacement contains &, use \& to escape
text="Hello"
echo "$text" | sed 's/Hello/[&]/'
# Output: [Hello]
Method 3: Using tr Command
The tr command translates or substitutes characters efficiently. It works best for single character replacements.
text="hello"
echo "$text" | tr 'h' 'H'
# Output: Hello
Replace Multiple Characters
text="hello world"
echo "$text" | tr 'helo' 'HELO'
# Output: HEllo worlD
Remove Characters
text="a1b2c3"
echo "$text" | tr -d '0-9'
# Output: abc
Squeeze Repeated Characters
text="aabbccdd"
echo "$text" | tr -s 'abcd'
# Output: abcd
Method 4: Using awk
awk is excellent for conditional replacements and complex transformations.
text="one two three"
echo "$text" | awk '{gsub(/two/, "TWO"); print}'
# Output: one TWO three
Replace Specific Field
text="john:engineer:50000"
echo "$text" | awk -F: '{$2="manager"; print}' OFS=:
# Output: john:manager:50000
Method 5: Using Perl (if available)
Perl offers powerful regex replacement with extended features.
text="hello world"
echo "$text" | perl -pe 's/hello/hi/'
# Output: hi world
Practical Examples
Example 1: Replace in File and Process
#!/bin/bash
input_file="$1"
output_file="$2"
if [ ! -f "$input_file" ]; then
echo "File not found"
exit 1
fi
# Replace multiple patterns
sed -e 's/old_name/new_name/g' \
-e 's/deprecated/current/g' \
-e 's/TODO/DONE/g' \
"$input_file" > "$output_file"
echo "Replacements complete: $output_file"
Output:
$ bash script.sh old_config.txt new_config.txt
Replacements complete: new_config.txt
Example 2: Function for Safe String Replacement
#!/bin/bash
# Function for safe replacement with escaping
safe_replace() {
local string="$1"
local old_text="$2"
local new_text="$3"
# Escape special characters
old_text=$(printf '%s\n' "$old_text" | sed -e 's/[\/&]/\\&/g')
new_text=$(printf '%s\n' "$new_text" | sed -e 's/[\/&]/\\&/g')
echo "$string" | sed "s/$old_text/$new_text/g"
}
# Usage
result=$(safe_replace "path/to/file" "/" "|")
echo "$result" # Output: path|to|file
Example 3: Replace Multiple Patterns at Once
#!/bin/bash
text="The quick brown fox"
# Method 1: Chained replacements
result="$text"
result=${result/quick/slow}
result=${result/brown/white}
result=${result/fox/dog}
echo "$result" # Output: The slow white dog
# Method 2: Using sed with multiple -e options
echo "$text" | sed -e 's/quick/slow/g' -e 's/brown/white/g' -e 's/fox/dog/g'
# Output: The slow white dog
Example 4: Replace with Context
#!/bin/bash
log_line="2024-12-25 ERROR Database connection failed"
# Replace ERROR only when preceded by date
result=$(echo "$log_line" | sed 's/^\([0-9-]* \)ERROR/\1CRITICAL/')
echo "$result" # Output: 2024-12-25 CRITICAL Database connection failed
Example 5: Conditional Replacement
#!/bin/bash
#!/bin/bash
text="foo123bar456"
# Only replace numbers followed by "bar"
result=$(echo "$text" | sed 's/\([0-9]*\)bar/[REPLACED]/g')
echo "$result" # Output: foo123[REPLACED]456
Example 6: Count Replacements
#!/bin/bash
text="apple apple apple"
# Count how many times pattern matches
count=$(echo "$text" | grep -o "apple" | wc -l)
echo "Pattern matches: $count" # Output: Pattern matches: 3
# Using sed to replace and count
result=$(echo "$text" | sed 's/apple/orange/g')
changes=$(echo "$text" | sed 's/apple/orange/g' | tr -cd 'o' | wc -c)
echo "Result: $result"
echo "Replacements made: $((count - changes))"
Performance Comparison
For replacing text in strings:
| Method | Speed | Use Case |
|---|---|---|
| Parameter expansion | Fastest | Simple string replacements |
| sed | Very Fast | Complex patterns, regex |
| awk | Fast | Field-based replacements |
| tr | Fast | Character-to-character replacement |
| Perl | Medium | Advanced regex features |
Recommendation: Use parameter expansion for quick replacements, sed for complex patterns.
Common Patterns
Email Obfuscation
email="john.doe@example.com"
# Replace domain with ***.***
obfuscated=$(echo "$email" | sed 's/@.*/***@***.***/')
echo "$obfuscated" # Output: john.doe***@***.***
URL Replacement
url="http://example.com/api/v1/users"
# Change domain
new_url=$(echo "$url" | sed 's|example.com|newdomain.com|')
echo "$new_url" # Output: http://newdomain.com/api/v1/users
Whitespace Normalization
text="hello world test"
# Replace multiple spaces with single space
normalized=$(echo "$text" | sed 's/ */ /g')
echo "$normalized" # Output: hello world test
Escape HTML Characters
text="<html> </html>"
# Escape for HTML
escaped=$(echo "$text" | sed 's/&/\&/g; s/</\</g; s/>/\>/g')
echo "$escaped" # Output: <html>&nbsp;</html>
Important Considerations
Special Characters in Replacement Text
When replacement text contains special characters like /, &, or \, they need escaping:
# Using / as delimiter in sed requires escaping
path="/home/user"
echo "path=/old" | sed "s|/old|$path|" # Use | as delimiter
# Output: path=/home/user
# Using & in replacement (refers to matched text)
echo "word" | sed 's/word/[&]/' # Output: [word]
Performance Tips
- Use parameter expansion for simple replacements in loops
- Use sed for file-based replacements (more efficient than reading into variable)
- Use tr for single character replacements
- Avoid unnecessary pipes when parameter expansion works
Key Points
- Parameter expansion
${var/old/new}is fastest for simple cases - Use
//for global replacement, single/for first only - sed is powerful for regex patterns and file operations
- tr is efficient for character substitutions
- Always escape special characters in sed patterns
- Test replacements on sample data before applying to production files
Quick Reference
# Replace first occurrence
${text/pattern/replacement}
# Replace all occurrences
${text//pattern/replacement}
# Using sed
sed 's/old/new/' # First occurrence
sed 's/old/new/g' # All occurrences
sed 's/old/new/gi' # Case-insensitive
# Using tr
tr 'old' 'new' # Character translation
# Escape special characters in sed
sed 's/\./\//g' # Replace . with /
Recommended Pattern
#!/bin/bash
# For simple replacements use parameter expansion
text="hello world"
result=${text/world/universe}
# For complex patterns use sed
text="The year is 2024"
result=$(echo "$text" | sed 's/[0-9]\{4\}/2025/g')
# For file operations use sed with -i
sed -i 's/old_value/new_value/g' config.txt