SED Address
Learning SED
Chapter 3 — Addresses and Line Selection
What Is an Address?
Every SED command runs against the pattern space — by default, on every line of the input. An address is a restriction you place before a command to limit which lines it acts on.
The general form is:
sed 'address command' file
If you provide no address, the command runs on all lines. If you provide an address, the command only runs when SED is processing a line that matches that address.
SED supports four types of address:
- Line number addresses — target a specific line by its position
- The last-line address (
$) — target the final line of input - Pattern addresses — target lines matching a regular expression
- Range addresses — target a span of lines between a start and end address
Addresses can be combined with negation (!) to target every line except those that match, and with step notation to target every Nth line.
Line Number Addresses
The simplest address is a plain line number. SED counts lines from 1:
# Delete line 1 (commonly used to strip a header) sed '1d' file.txt # Substitute only on line 5 sed '5s/old/new/' file.txt # Print only line 3 sed -n '3p' file.txt
The Last-Line Address: $
$ matches the very last line of the input. Because SED processes streams, it does not know in advance how many lines there are — $ is resolved dynamically as SED reads:
# Delete the last line sed '$d' file.txt # Append text after the last line sed '$a\--- end of file ---' file.txt # Substitute only on the last line sed '$s/old/new/' file.txt # Print only the last line (equivalent to tail -1) sed -n '$p' file.txt
$ the line address with $ the regex anchor. Inside a pattern address (/regex/), $ means end-of-line. As a standalone address before a command, $ means the last line of the file. Context tells SED which meaning applies.
Pattern Addresses
A pattern address matches any line where a regular expression is found. Enclose the regex in forward slashes:
# Delete all lines containing "DEBUG" sed '/DEBUG/d' logfile.txt # Substitute only on lines containing "ERROR" sed '/ERROR/s/server1/server2/' logfile.txt # Print only lines starting with a digit sed -n '/^[0-9]/p' file.txt # Delete blank lines sed '/^$/d' file.txt # Delete lines containing only whitespace sed '/^[[:space:]]*$/d' file.txt
Changing the pattern address delimiter
Just like the substitute command, pattern addresses can use an alternative delimiter. This is essential when the regex itself contains forward slashes (such as file paths):
# Delete lines containing a file path — awkward with / sed '/\/usr\/local\/bin/d' file.txt # Same thing using \| as alternate delimiter — much cleaner sed '\|/usr/local/bin|d' file.txt # Using # as delimiter sed '\#/etc/nginx#d' file.txt
\delimregexdelim. This is different from the substitute command where you just start with the delimiter directly after s.
Range Addresses
A range address targets every line between a start address and an end address (inclusive). Start and end are separated by a comma:
start,end command
Both start and end can independently be a line number, $, or a pattern address — and they can be mixed:
Line number ranges
# Delete lines 5 through 10 (inclusive) sed '5,10d' file.txt # Print lines 20 to 30 sed -n '20,30p' file.txt # Substitute from line 3 to end of file sed '3,$s/old/new/g' file.txt
Pattern ranges
When both addresses are patterns, SED activates the range when the start pattern first matches, and deactivates it when the end pattern next matches. Lines between those two — including the matching lines themselves — are all addressed:
# Delete everything between (and including) START and END markers sed '/START/,/END/d' file.txt # Substitute inside an XML/HTML block sed '/<config>/,/<\/config>/s/false/true/g' settings.xml # Comment out a block of lines in a shell script sed '/^# BEGIN BLOCK/,/^# END BLOCK/s/^/#/' script.sh
Mixed ranges (line + pattern)
# From line 1 up to (and including) the first blank line sed -n '1,/^$/p' file.txt # From the line matching "BEGIN" to line 50 sed '/BEGIN/,50d' file.txt # From line 5 to the line matching "DONE" sed -n '5,/DONE/p' file.txt
The range end-pattern pitfall
# If START and END appear on the same line, range stays open longer than expected printf 'before\nSTART END here\nafter\n' | sed '/START/,/END/d' # Output: before # "after" is ALSO deleted because the range never closed on the START END line
Step Addresses
GNU sed extends the addressing syntax with step addresses, written as first~step. This matches line first, then every stepth line after that:
# Match every other line starting from line 1 (odd lines: 1, 3, 5, …) sed -n '1~2p' file.txt # Match even lines (2, 4, 6, …) sed -n '2~2p' file.txt # Match every 3rd line starting from line 3 (3, 6, 9, …) sed -n '3~3p' file.txt # Delete every 5th line (5, 10, 15, …) — e.g. strip blank separator lines sed '5~5d' file.txt # Add a separator after every 4th line (useful for visual grouping) sed '4~4a\---' file.txt
awk 'NR%2==1'.
Negating an Address with !
Appending ! immediately after an address (before the command) inverts the match — the command runs on every line that does not match the address:
# Print every line EXCEPT line 1 (skip the header) sed -n '1!p' file.txt # Delete every line that does NOT contain "KEEP" sed '/KEEP/!d' file.txt # Substitute on every line except the last sed '$!s/old/new/g' file.txt # Substitute on every line outside a block sed '/START/,/END/!s/foo/bar/' file.txt # Negate a range — act on lines OUTSIDE the range sed '5,10!s/foo/bar/' file.txt
/regex/!d (delete lines that do not match) is equivalent to grep regex but processes the file in a SED pipeline — useful when you need the output to feed directly into more SED commands.
Multiple Commands on One Address — Braces
You can apply several commands to the same address by grouping them in curly braces { }. All commands inside the braces run only when the address matches:
# On lines containing "ERROR": substitute AND print sed -n '/ERROR/ { s/ERROR/FAULT/ p }' logfile.txt # On lines 5–15: strip leading spaces AND remove trailing punctuation sed '5,15 { s/^[[:space:]]*// s/[[:punct:]]*$// }' file.txt # Inline with semicolons (same effect, less readable for complex cases) sed '/ERROR/ { s/ERROR/FAULT/; p; }' logfile.txt
Braces can also be nested — an outer address restricts a range, and an inner address further filters within it:
# Between START and END markers, only substitute on lines containing "host" sed '/START/,/END/ { /host/ s/server1/server2/ }' config.txt
Address Summary Table
| Address form | Matches | Example |
|---|---|---|
n |
Line number n | sed '5d' |
$ |
The last line | sed '$d' |
/regex/ |
Any line where regex matches | sed '/ERROR/d' |
\Xregex X |
Pattern with alternate delimiter X | sed '\|/usr|d' |
n,m |
Lines n through m inclusive | sed '3,7d' |
n,$ |
Line n through end of file | sed '5,$d' |
/pat1/,/pat2/ |
From first line matching pat1 to next line matching pat2 | sed '/BEGIN/,/END/d' |
n,/regex/ |
From line n to next line matching regex | sed '1,/^$/d' |
first~step |
Line first, then every stepth line (GNU) | sed '1~2d' |
addr! |
All lines that do not match addr | sed '/KEEP/!d' |
addr { cmds } |
Multiple commands under one address | sed '/ERR/{s/E/e/;p}' |
Practical Recipes
Remove a CSV or TSV header row
# Skip the first line (header), pass the rest to downstream commands sed '1d' data.csv | awk -F, '{print $2}'
Extract a block between two markers
# Print only the lines between (and including) BEGIN and END sed -n '/BEGIN/,/END/p' file.txt # Print the block but EXCLUDE the marker lines themselves sed -n '/BEGIN/,/END/ { /BEGIN/d; /END/d; p }' file.txt
Process only a specific function in a script
# Add debug echo before every line inside the "deploy" function sed '/^deploy()/,/^}/ { /^[[:space:]]\+[^}]/ s/^/echo "DEBUG: " #/ }' deploy.sh
Remove comments and blank lines
# Strip # comments and empty lines from a config file sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' nginx.conf
Operate only outside a quoted string block
# Substitute only on lines that do NOT look like SQL string literals sed "/^'/!s/NULL/0/g" dump.sql
Print line numbers alongside matching lines
# Equivalent of grep -n "pattern" but done entirely in SED sed -n '/ERROR/ {=; p}' logfile.txt # = prints the current line number; p prints the line 42 2024-01-15 ERROR connection refused
Apply edits from line N to end of file (skip boilerplate header)
# A generated file has a 10-line header that must not be touched sed '11,$s/v1/v2/g' generated.txt
Common Mistakes with Addresses
Confusing line 0 and line 1
# SED counts from 1 — there is no line 0 sed '0d' file.txt # GNU sed: "invalid usage of line address 0" # Special case: 0,/regex/ — the only place 0 is valid # It means: start immediately (before reading line 1) # allowing the end-pattern to match on line 1 itself sed '0,/pattern/d' file.txt # Deletes from start up to AND including the first line matching /pattern/ # even if that line is line 1
Pattern ranges that never close
# If END never appears after START, the range stays open to EOF printf 'a\nSTART\nb\nc\n' | sed '/START/,/END/d' a # Lines b and c are also deleted — the range never closed # Always test on sample input before running on a real file
Forgetting that ranges are re-evaluated
# A pattern range matches EVERY occurrence of the start pattern printf 'START\na\nEND\nSTART\nb\nEND\n' | sed '/START/,/END/d' # Both blocks are deleted — the range activates twice # If you only want the FIRST block, use 0,/END/ after the START line
Quick Reference — Chapter 3
Address Types
Range and Step
Modifiers and Grouping
-n with p lets you use SED as a powerful line-extraction tool, and how q and Q let you stop processing early for efficiency on large files.