Searching and Replacing
grep finds lines matching a pattern. It can’t change them. sed — short for “stream editor” — takes that same regex knowledge and uses it to actually transform text, line by line, without ever opening a file in an editor. It’s the tool you reach for when you know exactly what needs to change across a file (or a hundred files) and typing it out by hand isn’t realistic.
The Core Idea: A Stream Editor
sed reads input one line at a time, applies whatever editing instruction you gave it, and prints the result. By default, it doesn’t touch the original file at all — it just prints the edited version to your terminal, the same way cat prints a file’s contents unedited.
sed 's/old/new/' notes.txtThis prints notes.txt to your terminal with the first occurrence of “old” on each line replaced by “new” — but notes.txt itself is completely untouched. Run cat notes.txt afterward and you’d see the original, unedited content still sitting there. This distinction matters enough that we’ll come back to it explicitly before covering how to actually save changes.
Substitution: The Command You’ll Use Constantly
That s/old/new/ from above is the substitute command, and it’s responsible for the vast majority of everything sed gets used for. Its shape is:
s/pattern/replacement/s— the substitute commandpattern— what to search for (a regular expression, exactly likegrep)replacement— what to replace each match with
sed 's/error/warning/' logfile.txtEvery line containing “error” gets its first match replaced with “warning.” Note “first” — by default, sed only replaces the first match on each line, even if a line contains “error” twice.
Replacing Every Match, Not Just The First
To replace every occurrence on a line, not just the first, add the g flag (global) after the closing slash:
sed 's/error/warning/g' logfile.txtNow every “error” on every line becomes “warning,” not just the first one per line. Forgetting g is one of the most common sed mistakes — a substitution that looks like it “isn’t working” is very often working correctly, just only on the first match per line.
Because The Pattern Is Regex, Everything From grep Applies
Since sed’s pattern is a regular expression, everything from the grep chapter carries over directly:
sed 's/^#//' config.txtThis strips a leading # from the start of any line that begins with one, using the same ^ anchor from the grep file. And the same caution about . matching any character, or * repeating the preceding character, applies identically here — sed isn’t a different pattern language, it’s the same one you already know, aimed at replacing text instead of just finding it.
Protip
As grep, sed also supported Exteded Regex pattern, just provide the same -E option.
Case-Insensitive Substitution
Just like grep -i, sed supports case-insensitive matching with the i flag, placed alongside g if you’re using both:
sed 's/error/warning/gi'This replaces every case variation of “error” — “Error,” “ERROR,” “error” — with “warning,” everywhere on each line.
Editing A File In Place: -i
Everything so far only prints the edited result — the original file is never touched. To actually apply the change to the file itself, add -i (in-place):
sed -i 's/error/warning/g' logfile.txtNow logfile.txt itself is modified directly, with no separate output shown at all.
Warning
-i overwrites the original file with no backup and no confirmation — the same category of risk as > overwriting a file, or rm deleting one. Before running any sed -i command, run the exact same command without -i first and actually read the output, confirming it does what you expect. Once -i runs, the original content is gone.
Some versions of sed support taking a backup automatically by giving -i a suffix:
sed -i.bak 's/error/warning/g' logfile.txtThis saves the original as logfile.txt.bak before editing logfile.txt in place — a genuinely useful habit while you’re still building confidence with -i, similar to the -i (interactive) flag suggested for rm and mv earlier in this section.
Restricting Substitution To Specific Lines
By default, sed applies your command to every line. You can restrict it to specific lines by adding a line number or range before the command:
sed '3s/error/warning/' logfile.txtThis only substitutes on line 3. A range works the same way:
sed '1,5s/error/warning/' logfile.txtThis substitutes only within lines 1 through 5, leaving everything else in the file untouched — useful when you know a change only applies to a specific section, like a header block, rather than the whole file.
Deleting Lines
Substitution isn’t the only thing sed does — d deletes lines matching a pattern entirely, rather than editing their content:
sed '/^$/d' notes.txtThis deletes every blank line (a line matching “start of line immediately followed by end of line” — in other words, nothing in between). Like substitution, d can be combined with -i to apply the deletion directly to the file, and with the same caution.
Inserting Lines: The i Command
s and d both act on lines that already exist. i (insert) adds a brand new line before a matching line, without touching anything already there.
sed '1iHi' fileThe 1 here is a line-address, exactly like the line-restriction syntax from earlier in this file — but instead of restricting where a substitution applies, it tells i exactly where to insert. This inserts a new line containing Hi immediately before line 1, pushing the original first line down. Note there’s no / around the text here — unlike s/pattern/replacement/, i just takes the literal text to insert directly after it, with no delimiters.
Inserting Lines By Pattern
You can insert relative to a pattern match instead of a fixed line number, using the same /pattern/ addressing style from grep and the rest of this file:
sed '/new/iHello' fileThis finds every line containing “new” and inserts a new line reading Hello immediately before each match — genuinely useful when you don’t know (or don’t want to hardcode) a specific line number, and instead want to insert relative to content that might move around as a file changes over time.
Note
Older, strictly POSIX-compliant versions of sed require a backslash and newline after i instead of letting you write the text directly on the same line — you may see sed '1i\' followed by the text on its own line in older scripts or documentation. GNU sed (the version on Debian/Ubuntu) accepts the simpler same-line form shown above, but recognize the backslash form if you encounter it elsewhere.
Appending Lines: The a Command
a (append) is i’s counterpart — it inserts a new line after a match instead of before it. Same addressing rules apply:
sed '1aHi' fileInserts Hi immediately after line 1, rather than before it.
Appending Lines By Pattern
sed '/new/aHello' fileInserts Hello immediately after every line containing “new.”
Replacing An Entire Line: The c Command
c (change) replaces a matched line’s entire content wholesale, rather than substituting just part of it the way s does.
sed '2cThis line has been replaced' fileThis discards whatever line 2 originally contained and puts the new text in its place entirely. c is the right tool when you want to replace a whole line regardless of what it currently says; s is the right tool when you’re targeting a specific piece of text within a line and want to leave the rest of it intact.
Replacing A Line By Pattern Instead Of Line Number
The line-number form of c above only helps when you know exactly which line number needs replacing — often you don’t, or the file changes over time and a fixed number won’t stay accurate. c accepts the same /pattern/ addressing as i and a:
sed '/error/cThis line has been replaced' fileThis replaces the entire content of every line containing “error” with the new text — not just the matched portion, the whole line, same as the line-number form, just targeted by content instead of position. This is the version you’ll actually reach for most of the time; the line-number form is really only useful when a file has a fixed, predictable structure you already know in advance.
Tip
If you only want to replace part of a line where a pattern matches — not the whole line — that’s s, not c. sed '/error/s/old/new/' restricts a substitution to only lines containing “error,” changing just the matched piece, while sed '/error/cNew text' throws away everything on that line and replaces it wholesale. Easy to reach for the wrong one if you’re thinking “match this line” without also thinking about how much of it you actually want gone.
Remember: Still Just Printing Unless You Add -i
Same rule as s and d earlier in this chapter: i, a, and c all just print the edited result to your terminal by default, leaving the original file untouched, until you add -i to apply the change directly. The same -i overwrite caution from earlier applies with exactly the same weight here.
A Quick Comparison With grep
| Scope | grep | sed |
|---|---|---|
| Finds matching lines | Yes | Yes (implicitly, before acting) |
| Prints matching lines as-is | Yes | No — transforms them |
| Can modify file content | No | Yes (with -i) |
| Can delete lines | No (only filters what’s shown) | Yes |
Think of grep as finding, sed as finding and changing — and since both speak the same pattern language, moving from one to the other is mostly about intent, not learning something new.
Piping Into sed
Like every other tool in this section, sed works just as well reading from a pipe as it does reading a file directly:
cat logfile.txt | grep "error" | sed 's/error/warning/g'This chain filters logfile.txt down to lines containing “error,” then replaces that word with “warning” on each of those lines — three tools, each doing one job, combined into a single pipeline. This kind of chaining is exactly what the pipe concept from a couple of files ago was building toward.