The sed command in Linux
sed (stream editor) transforms text as it flows through: its overwhelmingly common use is find-and-replace with the s command — sed 's/old/new/g'. Applied with -i it edits files in place, which makes it the standard tool for scripted config changes across servers. For anything more elaborate than substitution and line filtering, awk usually reads better.
How sed works
sed is a machine with a one-line buffer. For each input line: copy it into the pattern space, run every command of your script over that space in order, then (unless -n) print whatever the space holds and move on. All the commands — s for substitute, d for delete, p for print, plus line and regex addresses that gate them — operate on that single evolving line. One pass, no random access: a stream editor in the literal sense.
There is a second register, the hold space, that survives across lines — the mechanism behind the famous multi-line one-liners — but the everyday subtlety is elsewhere: -i does not edit "in place". sed writes a temporary file and renames it over the original, which means the file gets a new inode; hard links to it detach and, without care, permissions can shift. It is atomic and safe, but "in place" is a rename in disguise.
Syntax
sed [OPTIONS] 'COMMAND' [FILE...] Common options
| Option | What it does |
|---|---|
s/old/new/ | Substitute the first match on each line. |
s/old/new/g | Substitute every match on each line (g = global). |
-i | Edit the file in place instead of printing. |
-i.bak | In-place, but keep a backup with the given extension first. |
-n + p | Print only selected lines: sed -n "5,10p" shows lines 5–10. |
/pattern/d | Delete matching lines. |
-E | Extended regular expressions. |
How to use sed: examples
$ sed 's/http:/https:/g' links.txt Replace every http: with https: and print the result (file untouched).
$ sed -i 's/DEBUG=true/DEBUG=false/' .env Edit a config file in place — the scripted-deployment staple.
$ sed -i.bak 's/localhost/db.internal/g' config.yml Same, but keep config.yml.bak as a safety net.
$ sed -n '20,40p' app.log Print only lines 20 through 40 of a file.
$ sed '/^#/d; /^$/d' nginx.conf Strip comments and blank lines to read the effective configuration.
$ echo 'user_name' | sed -E 's/_(.)/\U\1/g' Regex groups and case conversion: snake_case → camelCase (GNU sed).
Real-world use cases for sed
Config surgery across a fleet
Changing a setting on twenty servers: ssh each and run sed -i.bak "s/^worker_processes.*/worker_processes 8;/" /etc/nginx/nginx.conf. Anchored to the line start, in-place with backup — scripted config management before (and beneath) Ansible.
Mass-updating source code
A domain migration: grep -rl "old-cdn.example.com" src/ | xargs sed -i "s/old-cdn/new-cdn/g" rewrites every affected file in one pipeline. grep selects, xargs distributes, sed edits — a pattern worth keeping in your notes verbatim.
Pro tips and common mistakes
- Always dry-run first: run the sed without -i and eyeball the output, or use -i.bak so originals survive.
- Change the delimiter when patterns contain slashes: s|/var/www|/srv/web|g — any character after s works.
- Anchor replacements to avoid collateral hits: s/^PORT=.*/PORT=8080/ only touches lines starting with PORT=.
- & in the replacement refers to the whole match: s/error/[&]/g wraps every occurrence in brackets.
Frequently asked questions about sed
How do I replace text in a file with sed?
sed -i 's/old/new/g' file — s substitutes, g applies to all matches per line, -i writes the file in place. Test without -i first to preview the result.
How do I use a / inside the pattern?
Pick a different delimiter — sed accepts any character after s: sed "s|/var/www|/srv/web|g" avoids escaping every slash.
Why does sed -i behave differently on macOS?
BSD sed (macOS) requires an argument to -i, even empty: sed -i '' 's/a/b/' file. GNU sed (Linux) takes -i alone. Scripts meant for both should use -i.bak.
Related commands
Find lines matching a pattern in files or piped input — the workhorse of text search.
awkPull columns out of any output, filter rows, and compute sums — the shell's spreadsheet.
cutSlice fields or character ranges out of each line — quick column extraction for delimited data.