Portal | Level: L1: Foundations | Topics: sed, Bash / Shell Scripting | Domain: CLI Tools
sed — The Stream Editor Primer¶
Why This Matters¶
sed is the original Unix stream editor, built for one job: transforming text as it flows through a pipe. Every log file, config file, and command output you encounter in operations is text that sed can slice, substitute, and reshape without opening an editor or writing a script. It composes with pipes, it works on streams, and it is available on every Unix system you will ever touch.
If you cannot reach for sed when you need a quick find-and-replace or a surgical config edit, you are either writing throwaway Python scripts for five-second problems or doing it by hand. Neither scales.
Mental Model¶
sed reads input line by line into a pattern space, applies commands to it, and prints the result. It never opens a file in an editor — it processes a stream. Think of it as a programmable find-and-replace that operates on every line flowing through a pipe.
The basic cycle:
1. Read a line into the pattern space
2. Apply all commands whose addresses match
3. Print the pattern space (unless -n suppresses it)
4. Clear the pattern space and repeat
Address Selection¶
Every sed command can be prefixed with an address that limits which lines it applies to.
# Line number: apply only to line 5
sed '5s/old/new/' file.txt
# Line range: lines 10 through 20
sed '10,20s/old/new/' file.txt
# Regex: lines matching a pattern
sed '/ERROR/s/old/new/' file.txt
# Regex range: from first match to second match
sed '/BEGIN/,/END/d' file.txt
# Last line
sed '$d' file.txt
# Every 3rd line starting from line 1 (GNU sed)
sed '1~3s/old/new/' file.txt
# Negation: all lines NOT matching
sed '/^#/!s/foo/bar/' file.txt
Fun fact: sed was written by Lee McMahon at Bell Labs in 1973-1974, making it one of the oldest Unix tools still in daily use. It was designed as a non-interactive version of the
ededitor — the name literally means "stream editor." Thes/old/new/syntax comes directly fromed's substitute command. This same syntax later influenced Perl, vim, and even modern IDE find-and-replace.Remember: sed's default behavior: read, execute, print, repeat. The
-nflag suppresses auto-print, so only explicitpcommands produce output. This is the key to using sed as a filter:sed -n '/pattern/p'acts like grep.
The Substitute Command¶
s/pattern/replacement/flags is the command you will use 90% of the time.
# Basic substitution (first occurrence per line)
sed 's/error/ERROR/' logfile.txt
# Global: all occurrences per line
sed 's/error/ERROR/g' logfile.txt
# Case-insensitive (GNU sed)
sed 's/error/ERROR/gI' logfile.txt
# Replace only the 2nd occurrence per line
sed 's/foo/bar/2' file.txt
# Print only lines where substitution happened
sed -n 's/error/ERROR/gp' logfile.txt
Regex: BRE vs ERE¶
By default, sed uses Basic Regular Expressions (BRE). This means +, ?, {, (, and | are literal characters unless escaped.
# BRE: grouping requires escaped parens
sed 's/\(foo\)\(bar\)/\2\1/' file.txt
# ERE with -E: grouping uses normal parens
sed -E 's/(foo)(bar)/\2\1/' file.txt
# BRE: one or more requires \+
sed 's/[0-9]\+/NUM/' file.txt
# ERE with -E: one or more uses +
sed -E 's/[0-9]+/NUM/' file.txt
Always use -E (extended regex) unless you are writing for ancient systems. It matches what you learned in every regex tutorial.
Gotcha: macOS ships BSD sed, which behaves differently from GNU sed on Linux. The biggest difference:
-i(in-place) on macOS requires an explicit backup extension:sed -i '' 's/old/new/' file(empty string = no backup). On Linux,sed -i 's/old/new/' fileworks without the extra argument. Scripts that work on Linux break on macOS and vice versa. For portability, always usesed -i.bak(works on both) or useperl -pi -einstead.
In-Place Editing¶
# Edit file in place (GNU/Linux)
sed -i 's/old/new/g' file.txt
# Edit file in place (macOS / BSD) — requires backup suffix
sed -i '' 's/old/new/g' file.txt
# In-place with backup
sed -i.bak 's/old/new/g' file.txt
# Creates file.txt.bak with original content
Delete, Insert, Append¶
# Delete lines matching a pattern
sed '/^#/d' config.txt # Remove comment lines
sed '/^$/d' file.txt # Remove blank lines
sed '1,5d' file.txt # Delete first 5 lines
# Insert BEFORE a matching line
sed '/\[production\]/i\# Added by deploy script' config.ini
# Append AFTER a matching line
sed '/\[production\]/a\new_setting = true' config.ini
# Change (replace entire line)
sed '/^hostname=/c\hostname=prod-web-01' config.txt
Multi-Line Processing and Hold Space¶
Under the hood: The hold space was a brilliant design choice for 1974 — it gave sed a second register to store data between lines, enabling multi-line operations on a stream processor with minimal memory. The
h,H,g,G,xcommands shuttle text between the pattern space and hold space. Think of it as two clipboards: the pattern space is your active workspace, the hold space is your scratch pad. In practice, if you need more than basic hold-space operations, reach for awk or Perl.
sed has two buffers: the pattern space (current line) and the hold space (a scratch buffer).
# N: append next line to pattern space (separated by \n)
# Joins every pair of lines
sed 'N;s/\n/ /' file.txt
# H: append pattern space to hold space
# g: replace pattern space with hold space
# x: swap pattern space and hold space
# Reverse a file (tac equivalent)
sed -n '1!G;h;$p' file.txt
# Delete blank line after every pattern match
sed '/PATTERN/{n;/^$/d}' file.txt
# Print paragraphs containing PATTERN (paragraphs separated by blank lines)
sed -n '/PATTERN/,/^$/p' file.txt
For anything beyond basic hold-space work, consider awk or a real scripting language. Hold-space gymnastics are clever but unmaintainable.
Practical sed Patterns¶
One-liner: The most useful sed command for DevOps:
sed -i 's/^#\(.*SETTING.*\)/\1/' config.file— uncomment a specific setting in a config file without opening an editor. Pair it with Ansible'slineinfilemodule for fleet-wide config changes.
# Strip leading/trailing whitespace
sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' file.txt
# Remove HTML tags
sed -E 's/<[^>]+>//g' page.html
# Extract value from key=value lines
sed -n 's/^DB_HOST=//p' .env
# Comment out a line
sed '/^dangerous_setting/s/^/# /' config.txt
# Uncomment a line
sed 's/^# \(dangerous_setting\)/\1/' config.txt
# Replace text between markers (leave markers intact)
sed '/^---START---$/,/^---END---$/{ /^---/!d; }' file.txt
sed vs awk — When to Reach for Which¶
| Task | Tool | Why |
|---|---|---|
| Simple find-and-replace | sed | One-liner, no field parsing needed |
| Delete/insert lines | sed | Address-based line operations |
| Extract specific fields | awk | Built-in field splitting |
| Math on columns | awk | Has arithmetic operators |
| In-place file editing | sed | -i flag |
| Quick regex substitution in a pipe | sed | Lighter syntax for simple cases |
When the task involves fields, numbers, or any form of aggregation, reach for awk. When the task is substitution or line-level deletion, sed is your tool.
Interview tip: If asked "when would you use sed vs awk vs perl?", the clean answer is: sed for simple substitutions and line deletions (one-liners), awk for field-based extraction and column math, Perl for anything more complex. sed and awk are always installed; Perl usually is. Python is better for anything over ~5 lines. The goal is to pick the lightest tool that gets the job done.
Wiki Navigation¶
Prerequisites¶
- Linux Ops (Topic Pack, L0)
Related Content¶
- Advanced Bash for Ops (Topic Pack, L1) — Bash / Shell Scripting
- Bash Exercises (Quest Ladder) (CLI) (Exercise Set, L0) — Bash / Shell Scripting
- Bash Flashcards (CLI) (flashcard_deck, L1) — Bash / Shell Scripting
- Cron & Job Scheduling (Topic Pack, L1) — Bash / Shell Scripting
- Environment Variables (Topic Pack, L1) — Bash / Shell Scripting
- Fleet Operations at Scale (Topic Pack, L2) — Bash / Shell Scripting
- LPIC / LFCS Exam Preparation (Topic Pack, L2) — Bash / Shell Scripting
- Linux Ops (Topic Pack, L0) — Bash / Shell Scripting
- Linux Ops Drills (Drill, L0) — Bash / Shell Scripting
- Linux Text Processing (Topic Pack, L1) — Bash / Shell Scripting