Inverting a Match with !
Placing an exclamation mark immediately after an address negates it, so the command runs on every line the address does NOT match. If /foo/d deletes lines containing foo, then /foo/!d deletes every line that lacks foo, effectively keeping only the foo lines. The ! sits between the address and the command letter, and it works with single addresses, ranges, and step addresses alike. This inversion is one of sed's most compact tools for expressing 'everything except'.
Cricket analogy: Negation is a reverse sweep in selection: /foo/!d keeps only the foo lines the way a captain fields everyone except the non-strikers, dismissing all who don't fit the plan.
Negating Ranges and Steps
Negation composes with any address form. A negated numeric range like 2,4!d deletes every line outside lines 2 through 4, keeping only that middle block. A negated last-line address $!d deletes all lines except the last, a compact way to print just the final line's content after deletion. Because the ! attaches to the whole address, /A/,/B/!s/x/y/ substitutes on every line that falls outside the A-to-B region. Negation therefore turns any selection into its exact complement without rewriting the pattern.
Cricket analogy: Negating a range 2,4!d is like protecting the middle order and dismissing everyone else; only overs 2 to 4 survive while the rest of the innings is scrubbed.
# Keep only lines containing 'ERROR' (delete everything else)
sed '/ERROR/!d' log.txt
# Substitute on every line EXCEPT those inside a code block
sed '/```/,/```/!s/foo/bar/g' notes.md
# Print only the last line: delete all but the last, print what remains
sed -n '$!d; p' file.txt # or simply: sed -n '$p'
# Comment out every line that is NOT already a comment
sed '/^#/!s/^/# /' config.txt
# Delete every line outside lines 5-10
sed '5,10!d' file.txtThe idiom /pattern/!d is functionally the grep of sed: it discards non-matching lines, leaving a stream equivalent to grep 'pattern'. It is handy inside larger sed scripts where spawning grep would be awkward.
Placement and Common Mistakes
The ! must come after the address and before the command, with the address itself unaltered. Writing !/foo/d is a syntax error; the correct form is /foo/!d. GNU sed tolerates whitespace and even multiple exclamation marks (an even count cancels out), but POSIX only guarantees a single ! directly after the address. When combining with block braces, the ! applies to the whole group: /A/,/B/!{ s/x/y/; s/p/q/; } runs both substitutions on lines outside the range. Misplacing the ! inside the braces would negate only the first command.
Cricket analogy: Placing ! correctly is like appealing at the right moment: /foo/!d works, but !/foo/d is appealing before the ball is bowled, and the umpire rejects it outright.
When negation is paired with braces, position matters: /A/,/B/!{ cmd1; cmd2; } negates the whole block, but /A/,/B/{ cmd1!; cmd2; } negates only cmd1. Decide whether you want to invert the address or a single inner command.
- A trailing ! after an address negates it, running the command on non-matching lines.
- /pattern/!d keeps only matching lines, acting like grep inside sed.
- Negation works with single addresses, numeric ranges, $, and step addresses.
- $!d deletes everything but the last line; 2,4!d keeps only lines 2 through 4.
- The ! must sit between the address and the command; !/foo/d is a syntax error.
- With braces, /A/,/B/!{ ... } negates the entire block, not just one inner command.
Practice what you learned
1. What does sed '/foo/!d' do?
2. Which of these is the correct placement of the negation operator?
3. What does sed -n '$!d; p' effectively print?
4. In /A/,/B/!{ s/x/y/; s/p/q/; }, which lines get both substitutions?
5. How is /pattern/!d similar to a common tool?
Was this page helpful?
You May Also Like
Line Ranges and Steps
Selecting spans of lines in sed using numeric ranges, the $ last-line symbol, regex-bounded ranges, and GNU's first~step notation for periodic selection.
The Print and Delete Commands
How sed's p (print) and d (delete) commands work, why -n changes everything, and the difference between d and D in the multi-line pattern space.
Grouping Commands with Braces
Using { } to bind multiple sed commands to a single address so they execute together as a block, including nesting and separator rules.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics