sed (stream editor) reads input line-by-line, applies editing commands, and writes the result to stdout. It is the fastest tool for substitution, deletion, line insertion, and in-place file editing in shell scripts.
Quick reference
| Task | Command |
|---|---|
| Substitute first match per line | sed 's/old/new/' file |
| Substitute all matches per line | sed 's/old/new/g' file |
| Case-insensitive substitute | sed 's/old/new/gI' |
| Delete a line | sed '3d' file |
| Delete matching lines | sed '/pattern/d' file |
| Print line range | sed -n '5,10p' file |
| Insert line before match | sed '/match/i\new line' |
| Append line after match | sed '/match/a\new line' |
| Edit file in-place | sed -i 's/old/new/g' file |
| In-place with backup | sed -i.bak 's/old/new/g' file |
| Delete blank lines | sed '/^$/d' file |
| Print line numbers | sed -n '=' file |
Addresses
An address tells sed which lines to act on. Without an address, the command applies to every line.
| Address | Meaning |
|---|---|
3 |
Line 3 only |
3,7 |
Lines 3 through 7 |
3,+4 |
Line 3 and next 4 lines (GNU sed) |
$ |
Last line |
1~2 |
Every odd line (GNU sed step) |
0~2 |
Every even line (GNU sed step) |
/pattern/ |
Lines matching regex |
/start/,/end/ |
Lines from first match to second match |
/pattern/,+3 |
Matching line and next 3 |
! suffix |
Negate — apply to lines that don't match |
# Delete lines 2 to 5
sed '2,5d' file
# Print only lines matching "error"
sed -n '/error/p' file
# Delete all lines except those matching "keep"
sed '/keep/!d' file
# Apply substitution from line 10 to end
sed '10,$s/foo/bar/' file
Substitution (s command)
Syntax: s/regex/replacement/flags
Flags
| Flag | Meaning |
|---|---|
g |
Replace all occurrences on the line |
2 |
Replace 2nd occurrence only |
2g |
Replace 2nd and all subsequent |
I |
Case-insensitive (GNU sed) |
p |
Print if substitution made |
w file |
Write changed lines to file |
e |
Execute result as shell command (GNU sed) |
# Replace only the 2nd occurrence
sed 's/cat/dog/2' file
# Replace 2nd occurrence onward
sed 's/cat/dog/2g' file
# Print only lines where substitution happened
sed -n 's/foo/bar/p' file
# Collect all changed lines into a new file
sed -n 's/old/new/pw changed.txt' file
Backreferences and capture groups
Use \(group\) in BRE (default) or \(group\) in ERE with -E/-r:
# Swap first and second words
sed 's/\(\w\+\) \(\w\+\)/\2 \1/' file
# Same with extended regex (-E)
sed -E 's/(\w+) (\w+)/\2 \1/' file
# Wrap each number in brackets
sed -E 's/([0-9]+)/[\1]/g' file
# Extract domain from URL
sed -E 's|https?://([^/]+).*|\1|' urls.txt
Delimiter alternatives
When the pattern contains /, use any other character as delimiter:
# Replace a path — use | instead of /
sed 's|/usr/local|/opt|g' config
# Use # as delimiter
sed 's#http://old.example.com#https://new.example.com#g' file
Deletion and printing
# Delete line 5
sed '5d' file
# Delete last line
sed '$d' file
# Delete lines matching pattern
sed '/^#/d' file # remove comment lines
sed '/^[[:space:]]*$/d' file # remove blank / whitespace-only lines
# Delete range
sed '3,7d' file
# Print lines 5 to 10 (suppress other output with -n)
sed -n '5,10p' file
# Print last 5 lines (requires GNU sed)
sed -n '$p' file # just last line
tail -5 file # for last N, tail is simpler
# Print line numbers alongside content
sed -n '=' file | paste - <(cat file)
Insertion and appending
| Command | Effect |
|---|---|
i\text |
Insert text before the addressed line |
a\text |
Append text after the addressed line |
c\text |
Replace the addressed line with text |
# Insert a header before line 1
sed '1i\# Generated file — do not edit' file
# Append a footer after the last line
sed '$a\# End of file' file
# Insert a blank line before every line matching "Section"
sed '/^Section/i\\' file
# Replace line 5 entirely
sed '5c\REPLACED LINE' file
# Add a line after every match
sed '/TODO/a\ # Priority: medium' file
In-place editing
# Edit file directly (GNU sed)
sed -i 's/old/new/g' file.txt
# Edit with backup (GNU sed creates file.txt.bak)
sed -i.bak 's/old/new/g' file.txt
# macOS/BSD sed requires the suffix argument (can be empty string)
sed -i '' 's/old/new/g' file.txt
# Edit multiple files at once
sed -i 's/v1\.0/v2\.0/g' *.conf
# GNU sed portable pattern for scripts that run on both Linux and macOS
# Use a backup suffix and remove it afterwards
sed -i.bak 's/old/new/g' file && rm file.bak
Multiple commands
# Semicolon-separated (simple commands)
sed 's/foo/bar/g; s/baz/qux/g' file
# -e flag for each command
sed -e 's/foo/bar/g' -e '/^#/d' file
# Script file with -f
cat > fixes.sed <<'EOF'
s/foo/bar/g
/^#/d
s/[[:space:]]*$//
EOF
sed -f fixes.sed file
Hold space and multi-line tricks
sed has two buffers: pattern space (current line) and hold space (persistent store).
| Command | Action |
|---|---|
h |
Copy pattern space → hold space |
H |
Append pattern space → hold space |
g |
Copy hold space → pattern space |
G |
Append hold space → pattern space |
x |
Exchange pattern and hold space |
n |
Read next line into pattern space |
N |
Append next line to pattern space |
P |
Print up to first \n in pattern space |
D |
Delete up to first \n and restart |
# Reverse file order (tac equivalent)
sed -n '1!G;h;$p' file
# Join every two lines
sed 'N;s/\n/ /' file
# Delete duplicate consecutive lines (uniq equivalent)
sed '$!N;/^\(.*\)\n\1$/!P;D' file
# Print paragraph containing "error" (blank-line delimited)
sed -n '/error/{/^$/!{H;d};G;/error/p;d};/^$/!H;/^$/d' file
Character classes (POSIX)
| Class | Matches |
|---|---|
[[:alpha:]] |
Letters a-z A-Z |
[[:digit:]] |
Digits 0-9 |
[[:alnum:]] |
Letters and digits |
[[:space:]] |
Space, tab, newline, etc. |
[[:upper:]] |
Uppercase A-Z |
[[:lower:]] |
Lowercase a-z |
[[:punct:]] |
Punctuation |
[[:blank:]] |
Space and tab only |
# Remove trailing whitespace
sed 's/[[:space:]]*$//' file
# Collapse multiple spaces into one
sed 's/[[:space:]]\+/ /g' file
# Remove all non-printable characters
sed 's/[^[:print:]]//g' file
30+ practical one-liners
Text cleanup
# Remove trailing whitespace
sed 's/[[:space:]]*$//' file
# Remove leading whitespace
sed 's/^[[:space:]]*//' file
# Remove both leading and trailing whitespace (trim)
sed 's/^[[:space:]]*//;s/[[:space:]]*$//' file
# Collapse multiple blank lines into one
sed '/^$/N;/^\n$/d' file
# Remove blank lines entirely
sed '/^[[:space:]]*$/d' file
# Remove lines shorter than 5 characters
sed '/.\{5\}/!d' file
# Remove HTML tags
sed 's/<[^>]*>//g' file
# Remove comments (lines starting with #)
sed '/^[[:space:]]*#/d' file
Numbers and columns
# Add line numbers
sed = file | sed 'N;s/\n/\t/'
# Delete first 3 fields (space-separated)
sed 's/[^ ]* \{0,1\}//;s/[^ ]* \{0,1\}//;s/[^ ]* \{0,1\}//' file
# Print only lines containing a number
sed -n '/[0-9]/p' file
# Extract lines with IP addresses
sed -n '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/p' file
File and log processing
# View lines 20 to 30 of a large file (no head/tail needed)
sed -n '20,30p' file
# Remove first line (header)
sed '1d' file
# Remove last line
sed '$d' file
# Double-space a file (add blank line after each line)
sed G file
# Print between two patterns (inclusive)
sed -n '/START/,/END/p' file
# Print between two patterns (exclusive — skip the markers)
sed -n '/START/,/END/{/START/!{/END/!p}}' file
# Extract Apache/Nginx 500 errors
sed -n '/ 500 /p' access.log
# Redact passwords in config files (print to stdout)
sed 's/password=.*/password=REDACTED/' app.conf
Code editing
# Uncomment lines starting with //
sed 's|^[[:space:]]*//*||' file.js
# Add indentation (4 spaces) to every line
sed 's/^/ /' file
# Remove indentation (up to 4 spaces)
sed 's/^ //' file
# Wrap every line in double quotes (CSV quoting)
sed 's/.*/"&"/' file
# Replace Windows line endings (CRLF → LF)
sed 's/\r//' file
# Add semicolons to end of each line (JS/CSS)
sed 's/$/;/' file
GNU sed vs BSD sed (macOS)
| Feature | GNU sed (Linux) | BSD sed (macOS) |
|---|---|---|
| In-place flag | sed -i 's/a/b/' |
sed -i '' 's/a/b/' |
| Extended regex | -E or -r |
-E only |
| Case-insensitive | /I flag |
Not supported |
| Step addresses | first~step |
Not supported |
\w word chars |
Supported | Not supported — use [[:alnum:]_] |
\+ one-or-more |
BRE: \+, ERE: + |
Same |
\b word boundary |
Supported | Not supported — use [[:<:]] / [[:>:]] |
Cross-platform tip: Install GNU sed on macOS via Homebrew (brew install gnu-sed) and use it as gsed.
# POSIX-portable one-liner: trim trailing spaces
sed 's/[[:space:]]*$//' file # works everywhere
sed vs awk vs grep
| Tool | Best for |
|---|---|
grep |
Finding lines that match a pattern — output unchanged |
sed |
Transforming lines — substitution, deletion, insertion |
awk |
Structured data, field math, reports, per-field logic |
perl -pe |
Complex multi-line transforms, full regex engine |
# grep: find lines
grep 'ERROR' app.log
# sed: fix a URL across a file
sed 's|http://|https://|g' config.yaml
# awk: sum the 3rd column
awk '{sum += $3} END {print sum}' data.csv
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
sed -i 's/a/b/' on macOS |
BSD sed requires suffix | sed -i '' 's/a/b/' |
Using / in path patterns |
Delimiter conflict | Use | or # as delimiter |
\w on macOS BSD sed |
Not supported | Use [[:alnum:]_] |
Forgetting -n with p |
Prints every line + matches | Add -n to suppress default output |
s/\n/x/ doesn't work |
sed works line-by-line, \n not in pattern space |
Use N to join lines first |
No g flag in substitution |
Only first match replaced | Add g flag: s/old/new/g |
| Editing file while reading it | Corruption without -i |
Always use -i or redirect to temp file |
6 FAQ
Q: How do I replace a newline with a space?sed reads one line at a time, so \n is never in the pattern space. Use tr '\n' ' ' for a simple line-join, or use sed 'N; s/\n/ /' to join pairs of lines.
Q: Why does sed -i work on Linux but not macOS?
BSD sed (macOS default) requires a mandatory suffix argument for in-place editing, even if empty: sed -i '' 's/a/b/' file. GNU sed makes the suffix optional.
Q: How do I make sed case-insensitive?
With GNU sed, append I to the substitute flags: s/pattern/replacement/gI. BSD sed does not support this flag — use [Pp][Aa][Tt][Tt]ern or pipe through perl -pi -e 's/pattern/replacement/gi'.
Q: How do I print only the matched part (like grep -o)?sed -n 's/.*\(pattern\).*/\1/p' file — capture the part you want and replace the whole line with it.
Q: How do I process only lines between two patterns?
Use a range address: sed -n '/START/,/END/p' file. The matched lines are included. To exclude the markers: sed -n '/START/,/END/{/START/d;/END/d;p}' file.
Q: Can sed process binary files?
Not reliably — sed is designed for text. Use dd, xxd + sed + xxd -r, or a dedicated tool like hexedit for binary patching.