Skip to main content

sed

sed (Stream Editor) processes text line by line, applying editing commands like substitution, deletion, and insertion. It is the standard tool for find-and-replace operations on files and streams, used in scripts to modify config files, transform log output, and perform batch text transformations.

Understanding sed

What Is sed in Simple Terms

sed reads input line by line, applies transformation commands, and writes the result. The most common use is substitution: replace this text with that text. Unlike a text editor, sed works non-interactively — perfect for scripted config file modifications.

sed 's/localhost/prod-db.internal/g' config.template > config.prod — replaces every occurrence of localhost with prod-db.internal and saves to a new file. One command, zero manual editing.

◈ DIAGRAM
+------------------------------------------+
| Input stream (file or stdin) |
+------------------------------------------+
|
line by line
|
v
+------------------------------------------+
| sed 's/localhost/10.0.2.100/g' |
| Pattern space: current line |
| Apply substitution command |
| s/old/new/g -- replace all occurrences |
+------------------------------------------+
|
v
+------------------------------------------+
| Output stream (modified lines) |
| localhost -> 10.0.2.100 |
+------------------------------------------+

Practical Commands

Bash
## Basic substitution: s/old/new/
sed 's/localhost/10.0.2.100/' config.yaml
## Only replaces FIRST occurrence per line
## Global substitution (all occurrences per line)
sed 's/localhost/10.0.2.100/g' config.yaml
## Case-insensitive substitution
sed 's/error/ERROR/gI' /var/log/app.log
## In-place edit (modifies the file directly)
sed -i 's/debug: true/debug: false/' app.yaml
## In-place with backup
sed -i.bak 's/debug: true/debug: false/' app.yaml
## Creates app.yaml.bak with original content
## Delete lines matching pattern
sed '/^#/d' config.yaml ## remove comment lines
sed '/^$/d' config.yaml ## remove blank lines
sed '/DEBUG/d' app.log ## remove debug log lines
## Print specific lines
sed -n '10,20p' /var/log/app.log ## lines 10-20
sed -n '/ERROR/p' /var/log/app.log ## only error lines
## Insert line before pattern
sed '/^\[Service\]/i User=payment-svc' service.unit
## Append line after pattern
sed '/^\[Service\]/a Restart=on-failure' service.unit
## Multiple expressions with -e
sed -e 's/localhost/10.0.2.100/g' -e 's/5432/5433/g' config.yaml
## Replace using delimiter other than / (useful when pattern contains /)
sed 's|/old/path|/new/path|g' config.yaml
## Production patterns:
## Remove trailing whitespace
sed -i 's/[[:space:]]*$//' file.txt
## Extract lines between two patterns
sed -n '/START/,/END/p' file.txt
## Comment out a line matching pattern
sed -i '/PasswordAuthentication yes/s/^/#/' /etc/ssh/sshd_config

Troubleshooting

Symptom Command What to Check
Substitution not working Test without -i first Preview before in-place edit
Special chars not matching Escape: \. \* \/ Regex metacharacters
sed -i differs on macOS Use sed -i '' on macOS macOS sed requires explicit backup suffix
Common Mistake

Using sed -i without testing the expression first. Always run sed 's/old/new/g' file (without -i) to preview the output before modifying the file in place. One wrong regex can corrupt a config file.

Tip

When the pattern contains forward slashes (like file paths), use a different delimiter: sed 's|/etc/nginx|/etc/nginx-new|g'. sed accepts any character after s as the delimiter — |, ,, @, or # all work.

Frequently Asked Questions

Why do people reach for sed instead of just editing a file directly for scripted changes?

sed processes a stream non-interactively, which means the same substitution can run identically across hundreds of files or as part of an automated pipeline (CI script, config templating, log transformation) with no manual editing step. Its core strength is line-oriented substitution via regex — `sed 's/old/new/g'` — but it also supports deletion, insertion, and multi-line pattern-space operations, all scriptable and reproducible in a way manual editing never is.

What's the classic sed gotcha involving in-place editing across platforms?

GNU sed's `-i` flag takes an optional suffix argument directly (`sed -i.bak 's/x/y/'` or `sed -i ''` with no suffix), but BSD/macOS sed requires the suffix argument even when empty (`sed -i '' 's/x/y/'`) — a script written and tested on Linux frequently breaks silently or errors out on macOS because of this exact syntax difference. Scripts meant to be portable should detect the platform or avoid `-i` in favor of writing to a temp file and moving it.