SED's address system controls precisely which lines a command applies to. A command without an address runs on every line. A command with a single address runs only on the matching line. A command with two addresses (a range) runs on all lines between — and including — the two boundaries. Mastering address ranges lets you operate on blocks of text, config sections, and structured documents with surgical precision.
1
Numeric and regex address forms
BASH
# ── Numeric single addresses ──────────────────────────────
sed '1s/foo/bar/' # line 1 only
sed '$s/foo/bar/' # last line only
sed '0~2s/foo/bar/' # every even line (GNU): 2,4,6...
sed '1~2s/foo/bar/' # every odd line (GNU): 1,3,5...
# ── Regex single addresses ────────────────────────────────
sed '/^DB_HOST/s/=.*/=prod-db-01/' # change DB_HOST value
sed '/^#/d' # delete comment lines
sed '/^[[:space:]]*$/d' # delete blank lines
# ── Negated address: ! ─────────────────────────────────────
sed '/^#/!s/foo/bar/' # substitute except on comment lines
sed '1!s/foo/bar/' # substitute on all but first line
sed '/^#/!d' # keep ONLY comment lines (delete non-comments)
2
Range addresses — between two lines
BASH
# ── Numeric ranges ────────────────────────────────────────
sed '2,5d' # delete lines 2-5
sed '2,5s/old/new/' # substitute only in lines 2-5
sed -n '5,10p' # print only lines 5-10
sed '10,$d' # delete from line 10 to end
sed '1,10!d' # keep only first 10 lines
# ── Pattern ranges: /start/,/end/ ─────────────────────────
# Applies to all lines from first match of start to first match of end
sed '/\[database\]/,/\[/d' # delete [database] section in INI
sed '/BEGIN/,/END/d' # delete BEGIN...END block
sed -n '/BEGIN/,/END/p' # print only BEGIN...END block
# ── Mixed numeric-regex range ─────────────────────────────
sed '1,/PATTERN/d' # delete from line 1 to first PATTERN
sed '/PATTERN/,$d' # delete from first PATTERN to end
# ── Grouped commands with {} ──────────────────────────────
sed '/\[database\]/,/\[/{
/^host/s/=.*/=prod-db-01/
/^port/s/=.*/=5432/
}' config.ini
3
Real-world range address patterns
BASH
# ── Update a config section ────────────────────────────────
# Config file with [section] headers:
# [database]
# host = old-host
# port = 5432
# [server]
# ...
sed '/^\[database\]/,/^\[/{
s/^host = .*/host = prod-db-01/
s/^port = .*/port = 5432/
}' config.ini
# ── Extract sections ──────────────────────────────────────
# Get only the [network] section
sed -n '/^\[network\]/,/^\[/p' config.ini | sed '$d'
# ── Delete an INI section completely ─────────────────────
# Delete [deprecated] section and its contents
sed '/^\[deprecated\]/,/^\[/{/^\[deprecated\]/!{/^\[/!d}}
/^\[deprecated\]/d' config.ini
# Simpler — using gnu sed 0 address: ─────────────────────
sed '/^\[deprecated\]/,/^\[/{/^\[deprecated\]/d; /^\[/!d}' config.ini
# ── Replace a block of text ───────────────────────────────
# Replace everything between MARKER_START and MARKER_END
sed '/MARKER_START/,/MARKER_END/{
/MARKER_START/!{/MARKER_END/!d}
/MARKER_END/i\ new_content_line_1\n new_content_line_2
}' template.txt
# ── Comment out a block ───────────────────────────────────
sed '/^server_block_start/,/^server_block_end/s/^/#/' nginx.conf
# ── Uncomment a block ─────────────────────────────────────
sed '/^#server_block_start/,/^#server_block_end/s/^#//' nginx.conf
4
Practical config file editing patterns
BASH
# ── Update a single key=value in a config ─────────────────
update_config() {
local file="${1}" key="${2}" value="${3}"
if grep -q "^${key}=" "${file}"; then
sed -i "s|^${key}=.*|${key}=${value}|" "${file}"
else
echo "${key}=${value}" >> "${file}"
fi
}
update_config /etc/myapp/config.env DB_HOST prod-db-01
update_config /etc/myapp/config.env LOG_LEVEL WARN
# ── Insert a line after a match ──────────────────────────
sed -i '/^worker_processes/a worker_rlimit_nofile 65536;' nginx.conf
# ── Insert a block after a match ─────────────────────────
sed -i '/^http {/a \ include /etc/nginx/conf.d/*.conf;\n include /etc/nginx/sites-enabled/*;' nginx.conf
# ── Remove a line from a file ─────────────────────────────
sed -i '/^old_setting=/d' config.env
# ── Append to a specific section ─────────────────────────
sed -i '/^\[server\]/,/^\[/{
/^\[server\]/a\ new_option = value
}' config.ini
vriddh@prod-01:~/scripts$sed -n '/^\[database\]/,/^\[/p' config.ini | head -5
[database]
host = prod-db-01
port = 5432
name = myapp
vriddh@prod-01:~/scripts$sed -i 's/^DB_HOST=.*/DB_HOST=prod-db-01/' config.env && grep DB_HOST config.env
DB_HOST=prod-db-01
█
✔ Address range rules — Always test range patterns with
-n '/start/,/end/p' before using -i to edit in place. Pattern ranges are inclusive — both the start and end lines are included. A !/ negates the address. Group multiple commands for a range with /start/,/end/{ cmd1; cmd2 }. Use | as the delimiter in s commands when the pattern contains / to avoid escaping: s|/old/path|/new/path|.