What the hell is sed and why should you care?
sed is the stream editor — it’s a command-line tool that reads text one line at a time, applies transformations based on patterns you give it, and spits out the result. Think of it as a scalpel for text files: surgical precision, no fluff.
Here’s the thing: if you’re doing anything with configuration files, logs, or bulk text changes, sed will save you hours of manual editing. It’s been around since the 1970s for a reason. Your future self at 2 AM will appreciate not having to open a file in vim and search-replace 500 times.
The basic syntax (that 90% of your usage falls into)
The bread and butter of sed is the s command (substitute). Here’s the shape of it:
sed 's/find/replace/' filename.txtThat’s it. Replace the first occurrence of “find” with “replace” on each line. But there’s more power hiding in those flags.
Flags that actually matter
The g flag — replace ALL occurrences on a line
sed 's/dog/cat/' pets.txtIf your line is “I have a dog and my dog has fleas,” only the first “dog” becomes “cat.”
sed 's/dog/cat/g' pets.txtNow both “dog”s become “cat”s. Use g by default — you almost always want it.
The i flag — case-insensitive
sed 's/Docker/podman/i' config.yamlMatches “docker”, “Docker”, “DOCKER”, and replaces with lowercase “podman” (the pattern is literal, only the match is case-insensitive).
The n flag — silent mode (print only matches)
By default, sed prints every line. The n flag suppresses automatic output, and p prints only lines that match. Useful for filtering:
sed -n 's/error/ERROR/p' app.logOnly prints lines that had the substitution.
Multiple expressions with -e
Want to chain multiple replacements?
sed -e 's/cats/sloths/g' -e 's/dogs/ducks/g' animals.txtEach -e is applied in order. This is cleaner than piping sed to sed (which works, but is inelegant).
Line addressing — replace only where you want
Specific line numbers
sed '5s/old/new/g' file.txtReplace only on line 5.
Line ranges
sed '2,7s/old/new/g' file.txtReplace on lines 2 through 7.
Pattern matching
sed '/^DEBUG/s/old/new/g' app.logReplace only on lines starting with “DEBUG”.
sed '/start/,/end/s/old/new/g' file.txtReplace between the first line matching “start” and the first line matching “end” (inclusive).
Deleting lines (the d command)
Sometimes you want to remove lines entirely:
sed '/^#/d' config.confDelete all comment lines (starting with #).
sed '10,20d' file.txtDelete lines 10–20.
Printing specific lines (the p command with -n)
sed -n '5,10p' file.txtPrint only lines 5–10 (like head -n 10 | tail -n 6, but shorter).
In-place editing with -i (the dangerous one)
By default, sed outputs to stdout. To actually overwrite the file:
sed -i 's/old/new/g' file.txtBut here’s the gotcha: macOS’s BSD sed is different from GNU sed. On macOS, you must provide a backup extension:
sed -i '' 's/old/new/g' file.txtWithout the empty string, BSD sed treats the script argument as the backup extension and the filename as the command, causing an error. Forget this once and you’ll be searching StackOverflow forever. Write it into your muscle memory now.
To be safe across systems, always use:
sed -i.bak 's/old/new/g' file.txtCreates a .bak backup on all platforms, which you can delete later (or keep, your call).
Dealing with special characters (the / problem)
If your find/replace contains slashes (like file paths), change the delimiter:
sed 's;/home/kingpin;/home/notkingpin;g' paths.txtOr use any other character that’s unlikely to appear in your data (;, |, #, @).
Real-world use cases
Strip comments from a config:
sed '/^#/d' nginx.conf | sed '/^$/d'Delete comment lines AND blank lines. Pipe it to tee to overwrite in place:
sed -i '/^#/d; /^$/d' nginx.confFix Windows line endings (CRLF to LF):
sed -i 's/\r$//' filename.txtExtract and clean up log lines:
sed -n '/ERROR/p' app.log | sed 's/ERROR:/[!]/g'Bulk rename variables in code:
sed -i 's/\bOLD_VAR\b/NEW_VAR/g' *.pyThe \b is a word boundary — matches “OLD_VAR” but not “OLD_VAR_SUFFIX”.
Quick cheatsheet
| Pattern | What it does |
|---|---|
sed 's/find/replace/' file | Replace first on each line |
sed 's/find/replace/g' file | Replace all on each line |
sed 's/find/replace/i' file | Case-insensitive |
sed '5s/find/replace/' file | Line 5 only |
sed '2,7s/find/replace/g' file | Lines 2–7 |
sed '/pattern/s/find/replace/g' file | Lines matching pattern |
sed '/start/,/end/s/find/replace/g' file | Between patterns |
sed '/pattern/d' file | Delete matching lines |
sed -n '5,10p' file | Print lines 5–10 |
sed -i.bak 's/find/replace/g' file | In-place with backup |
sed -e 's/a/b/g' -e 's/c/d/g' file | Multiple expressions |
That’s it. You now know enough sed to handle 95% of real-world situations. The man page is a rabbit hole of obscure features — you don’t need them yet. Start with s/find/replace/g, and level up only when you hit a wall.
Your config files (and your 2 AM self) will thank you.