sed (stream editor) reads text line by line, applies editing commands and prints the result. It is the tool for changing configuration files in scripts, rewriting URLs across a project, or trimming log output in a pipeline — anything you would otherwise open an editor for, but repeatable and automatable. This lesson covers substitution, addressing lines, deleting and inserting, and the in-place editing flag that makes sed a deployment workhorse.
sed takes a script of one or more commands and applies it to every line of input in turn. Each command has the form [address]command[options]. Without an address the command applies to every line; with one it applies only to matching lines. Input comes from files or standard input; output goes to standard output unless you use -i.
sed 's/cat/dog/' pets.txt # from a file
echo "one two" | sed 's/one/1/' # from a pipes/pattern/replacement/flags is what most people use sed for. The pattern is a regular expression (basic by default, extended with -E).
sed 's/error/ERROR/' app.log # first match on each line
sed 's/error/ERROR/g' app.log # every match on each line (g = global)
sed 's/error/ERROR/gi' app.log # case-insensitive too
sed 's/error/ERROR/2' app.log # only the second match per line
sed -E 's/([0-9]+)-([0-9]+)/\2-\1/' dates.txt # swap two groups
sed 's/.*"user":"\([^"]*\)".*/\1/' events.json # extract a value (BRE groups need \( \))& in the replacement stands for the whole match, and \1–\9 refer to capture groups:
echo "version 2.4" | sed -E 's/[0-9]+\.[0-9]+/v&/' # version v2.4The delimiter does not have to be /. When the pattern contains slashes — file paths, URLs — pick another character to avoid escaping:
sed 's#/var/www/old#/var/www/new#g' site.conf
sed 's|http://|https://|g' links.txt| Address | Selects |
|---|---|
| 5 | line 5 |
| 1,10 | lines 1 to 10 |
| $ | the last line |
| /regex/ | lines matching the pattern |
| /start/,/end/ | from a line matching start to the next matching end |
| 5,$ | from line 5 to the end |
| 1~2 | every second line, starting at 1 (GNU) |
| /regex/! | lines that do not match |
sed '3s/foo/bar/' file.txt # substitute only on line 3
sed '/^#/!s/localhost/0.0.0.0/' app.conf # skip comment lines
sed -n '/BEGIN/,/END/p' report.txt # print a block-n suppresses automatic output so that only lines you explicitly print appear. d deletes lines, i inserts before, a appends after and c changes a whole line.
sed -n '1,5p' file.txt # like head -5
sed -n '/ERROR/p' app.log # like grep ERROR
sed '/^$/d' file.txt # delete blank lines
sed '1d' data.csv # drop the header row
sed '$d' file.txt # drop the last line
sed '/DEBUG/d' app.log # drop debug lines
sed '1i # Generated file - do not edit' config.ini
sed '/\[Service\]/a Restart=always' app.service
sed '/^Port /c Port 2222' sshd_config-i writes the result back to the file. Give it a suffix to keep a backup, which costs nothing and has rescued many administrators.
sed -i 's/DEBUG=true/DEBUG=false/' .env
sed -i.bak 's/8080/80/g' docker-compose.yml # creates docker-compose.yml.bak
sed -i '' 's/foo/bar/' file.txt # macOS/BSD sed requires an explicit (empty) suffix
find . -name "*.md" -exec sed -i 's/Master/Main/g' {} +Preview first by running the same command without -i, or pipe it through diff:
diff <(cat .env) <(sed 's/DEBUG=true/DEBUG=false/' .env)Chain commands with -e, separate them with ;, or put them in a file with -f.
sed -e 's/foo/bar/g' -e '/^$/d' file.txt
sed 's/foo/bar/g; /^$/d' file.txt
sed -f cleanup.sed file.txtCommands run in order on each line before the next line is read, so a line deleted by an early command never reaches later ones.
g, so only the first occurrence on each line changes.+, ? or | without -E and getting a literal match./ inside a URL instead of switching the delimiter.sed -i on a symbolic link, which replaces the link with a regular file (add --follow-symlinks).sed to edit across lines; it works one line at a time unless you use the advanced N command. For multi-line structures prefer awk or a proper parser.Which command replaces all occurrences of "http" with "https" on every line of a file?
s/pattern/replacement/g is the core command; use -E for extended regex and \1 for groups.# or | when the pattern contains slashes.5, 1,10, $, /regex/, ranges) restrict commands to specific lines.-n with p prints selectively; d, i, a and c delete, insert, append and change lines.-i.bak edits in place with a backup; preview without -i first.Next lesson: awk: Column-Based Text Processing — treat every line as fields and compute sums, filters and reports.