What is `sed`?
sed (stream editor) processes text one line at a time. It reads input (a file or stdin), applies a sequence of editing commands, and prints the result — all without opening the file in a traditional editor.
It is irreplaceable for:
- Bulk find-and-replace across files
- Deleting or extracting specific lines
- Transforming log output or config files in pipelines
Basic Syntax
sed [OPTIONS] 'SCRIPT' [FILE...]
SCRIPT— one or more editing commands (usually quoted with'...')FILE— one or more input files; if omitted, reads from stdin
The Substitution Command `s`
The substitution command is by far the most common use of sed.
s/PATTERN/REPLACEMENT/FLAGS
PATTERN→ a regular expression to matchREPLACEMENT→ the text to put in place of the matchFLAGS→ optional modifiers (see below)
Replace First Match Per Line
sed 's/foo/bar/' file.txt
→ Replaces the first occurrence of foo on each line with bar.
Replace All Matches Per Line (`g` flag)
sed 's/foo/bar/g' file.txt
→ Replaces every occurrence on each line.
Case-Insensitive Match (`I` flag)
sed 's/error/ERROR/Ig' log.txt
→ Matches error, Error, ERROR, etc.
Replace Only the Nth Occurrence (`N` flag)
sed 's/foo/bar/2' file.txt
→ Replaces only the 2nd occurrence on each line.
Editing Files In Place (`-i`)
By default sed prints to stdout and leaves the original file untouched. The -i flag overwrites the file in place.
sed -i 's/localhost/127.0.0.1/g' config.conf
Always make a backup first:
sed -i.bak 's/localhost/127.0.0.1/g' config.conf
→ Saves the original as config.conf.bak before modifying.
**macOS note**: BSD `sed` requires an explicit suffix with `-i`. Use `-i ''` for no backup, or `-i.bak` for a backup.
Changing the Delimiter
The default delimiter is /. When your pattern or replacement contains slashes, swap to another character to avoid escaping.
# Replace a file path — using | instead of /
sed 's|/var/log|/logs|g' app.conf
# Same with # as delimiter
sed 's#http://old.example.com#https://new.example.com#g' links.txt
Any character can be used as the delimiter: |, #, @, ,, etc.
Addressing — Target Specific Lines
By default, sed applies commands to every line. An address before the command restricts it.
By Line Number
# Edit only line 5
sed '5s/foo/bar/' file.txt
# Edit lines 2 through 10
sed '2,10s/foo/bar/' file.txt
# Edit the last line
sed '$s/foo/bar/' file.txt
By Regex Pattern
# Edit only lines containing "ERROR"
sed '/ERROR/s/timeout/Timed Out/g' app.log
By Range (start/end pattern)
# Edit lines between START and END markers (inclusive)
sed '/START/,/END/s/foo/bar/g' file.txt
Negating an Address (`!`)
# Edit every line EXCEPT those matching "DEBUG"
sed '/DEBUG/!s/foo/bar/g' file.txt
Deleting Lines (`d`)
# Delete blank lines
sed '/^$/d' notes.txt
# Delete lines containing "TODO"
sed '/TODO/d' source.c
# Delete lines 1 through 5
sed '1,5d' file.txt
# Delete the last line
sed '$d' file.txt
Printing Specific Lines (`p` with `-n`)
By default sed prints every line. Use -n to suppress output and p to print only what you want.
# Print only lines 10 to 20
sed -n '10,20p' file.txt
# Print only lines matching a pattern
sed -n '/ERROR/p' app.log
# Print line numbers along with matching lines
sed -n '/WARN/{=;p}' app.log
→ = prints the current line number.
Inserting and Appending Lines
Insert Before a Line (`i`)
# Insert a comment before line 3
sed '3i\# This section is auto-generated' config.cfg
# Insert before every line matching a pattern
sed '/\[section\]/i\# --- section start ---' config.ini
Append After a Line (`a`)
# Append a blank line after every line matching "END"
sed '/END/a\\' file.txt
# Append a footer after the last line
sed '$a\# End of file' script.sh
Replace an Entire Line (`c`)
# Replace line 7 entirely
sed '7c\replaced content here' file.txt
Transliterate Characters (`y`)
y is like tr — it maps characters one-to-one.
# Convert lowercase a-z to uppercase A-Z
sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' file.txt
Multiple Commands
Semicolon-Separated
sed 's/foo/bar/g; s/baz/qux/g' file.txt
`-e` Flag (multiple `-e` options)
sed -e 's/foo/bar/g' -e '/^#/d' file.txt
Script File (`-f`)
For complex scripts, save commands in a file and reference it:
# cleanup.sed
s/foo/bar/g
/^$/d
s/ */ /g
sed -f cleanup.sed input.txt
Hold Space — Advanced Usage
sed has two buffers:
- Pattern space — the current line being processed
- Hold space — a secondary buffer you can save/restore
| Command | Meaning |
|---|---|
h | Copy pattern → hold |
H | Append pattern → hold |
g | Copy hold → pattern |
G | Append hold → pattern |
x | Exchange pattern and hold |
# Reverse the order of two lines
sed -n '1{h;d}; 2{G;p}' file.txt
Common Real-World Examples
Remove all comments from a config file:
sed '/^\s*#/d' nginx.conf
Strip trailing whitespace from every line:
sed 's/[[:space:]]*$//' file.txt
Add line numbers as a prefix:
sed '=' file.txt | sed 'N; s/\n/\t/'
Extract lines between two markers:
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' server.crt
In-place replace across multiple files:
sed -i.bak 's/v1\.0/v2\.0/g' README.md CHANGELOG.md docs/*.md
Remove ANSI color codes from a log:
sed 's/\x1b\[[0-9;]*m//g' colored.log
sed in Pipelines
sed is designed for pipelines — it reads from stdin and writes to stdout.
# Filter only ERROR lines then normalize the word "timeout"
grep 'ERROR' app.log | sed 's/timeout/Timed Out/g'
# Replace placeholders in a template and feed to a mailer
sed "s/{{NAME}}/Alice/g; s/{{DATE}}/$(date +%F)/g" template.txt | mail -s "Report" alice@example.com
# Extract HTTP status codes from an access log
awk '{print $9}' access.log | sort | uniq -c | sort -nr
Quick Reference
| Task | Command | |||
|---|---|---|---|---|
| Replace first match | sed 's/old/new/' | |||
| Replace all matches | sed 's/old/new/g' | |||
| Edit in place + backup | sed -i.bak 's/old/new/g' file | |||
| Delete blank lines | sed '/^$/d' | |||
| Delete lines matching regex | sed '/regex/d' | |||
| Print lines 5–15 | sed -n '5,15p' file | |||
| Print lines matching pattern | sed -n '/pattern/p' file | |||
| Insert line before match | sed '/match/i\new line' | |||
| Append line after match | sed '/match/a\new line' | |||
| Change delimiter | `sed 's\ | /old/path\ | /new/path\ | g'` |
| Run multiple commands | sed -e 'cmd1' -e 'cmd2' |
Gotchas
sed -iwithout a backup overwrites permanently — test withsed '...' file.txtfirst.- Extended regex (
-Eor-r) unlocks+,?,|,()without escaping — standardsedneeds\+,\?,\|,\(,\). - Greedy matching —
.matches as much as possible; use[^X]to stop at a character. - macOS vs GNU sed — use
sed -Eon macOS andsed -ron Linux for extended regex, or install GNU sed via Homebrew (gsed). - In-place with no backup on macOS requires
sed -i ''(empty string argument), not justsed -i.