Shell Scripting SED Address Ranges Advanced May 2026

Shell Scripting Advanced SED: Address Ranges

Master all SED address forms — line numbers, regex, start-to-end ranges, pattern-to-pattern ranges, step addresses, and negation. Apply complex multi-line block operations to extract, delete, and transform specific sections of files.

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.

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)
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
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
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
sed — address range editing
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|.