sed Cheatsheet

Addresses and Ranges

Use this sed reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Address forms

An address selects which lines a command runs on. Zero, one, or two addresses, depending on the command.

AddressSelectsPortable?
(none)every linePOSIX
Nline N, counted across all input filesPOSIX
$the last line of the last filePOSIX
/re/lines matching the BRE (or ERE with -E)POSIX
\cREcthe same, with c as the delimiterPOSIX
/re/Icase-insensitive matchboth, not POSIX
/re/Mmultiline matchGNU only
first~stepevery stepth line starting at firstGNU only
0,/re/up to the first match, even on line 1GNU only
addr1,addr2from addr1 through addr2POSIX
addr1,+Naddr1 plus the next N linesGNU, and works on macOS
addr1,~Naddr1 through the next line number divisible by NGNU only
addr!invert, every line the address does not selectPOSIX
sed -n '5p'          file    # line 5
sed -n '$p'          file    # last line
sed -n '/^Host/p'    file    # matching lines
sed -n '\|/etc/|p'   file    # alternate delimiter, no escaped slashes
sed -n '/warn/Ip'    file    # case-insensitive
sed    '5d'          file    # everything except line 5
sed -n '5!p'         file    # the same thing, inverted

Ranges

addr1,addr2 turns on at the first line matching addr1 and off at the next line matching addr2.

sed -n '10,20p'                file   # lines 10-20 inclusive
sed -n '10,$p'                 file   # line 10 to the end
sed -n '/BEGIN/,/END/p'        file   # inclusive block
sed -n '/BEGIN/,$p'            file   # from the marker to EOF
sed -n '2,+3p'                 file   # line 2 and the 3 after it
sed    '/^#/,/^$/d'            file   # comment block to the next blank line

The rules that trip people up:

- addr2 is tested starting on the line after addr1 matched. A single line matching both patterns still opens the range, and it closes later. This is the opposite of awk, where /a/,/b/ on one line is a one-line range. - If addr2 never matches, the range runs to end of input. - A range restarts. /A/,/B/ fires again every time A appears after a B. - If addr2 is a line number less than or equal to the line where addr1 matched, only that one line is selected. - 1,/re/ skips a match on line 1, because addr2 is tested from line 2. GNU's 0,/re/ exists precisely to fix that.

printf 'A\nx\nA\ny\n' | sed -n '1,/A/p'       # A x A   <-- surprising
printf 'A\nx\nA\ny\n' | gsed -n '0,/A/p'      # A       <-- GNU only
printf 'A\nx\n'       | sed -n '/A/,3p'       # A x     <-- addr2 clamps

Step and modulo addresses (GNU only)

gsed -n '1~2p'   file    # odd lines
gsed -n '0~2p'   file    # even lines
gsed -n '2~5p'   file    # line 2, 7, 12, ...
gsed -n '3,~4p'  file    # line 3 through the next multiple of 4

Portable equivalents, since macOS sed rejects ~ outright:

awk 'NR % 2'         file    # odd lines
awk 'NR % 2 == 0'    file    # even lines
sed -n 'p;n'         file    # odd lines, pure sed
sed -n 'n;p'         file    # even lines, pure sed

Negation

! after the address inverts it. It attaches to the address, not the command.

sed -n '3!p'            file   # everything but line 3
sed    '/^#/!d'         file   # keep ONLY comment lines
sed    '1,10!d'         file   # keep only the first 10 lines
sed -n '/BEGIN/,/END/!p' file  # everything outside the block
sed    '$!N'            file   # append the next line, except on the last line

$!N is the workhorse. It stops N from quitting early on a file with an odd line count, which is the difference between "joins pairs of lines" and "silently drops the last line".

Blocks and multiple commands

{ } groups commands under one address. On BSD sed every command inside the block, including the last one, must be terminated by ; or a newline.

# GNU: fine.  BSD: "extra characters at the end of p command"
sed -n '/^Host/{s/Host/H/;p}' config

# portable: note the semicolon before the closing brace
sed -n '/^Host/{s/Host/H/;p;}' config

# also portable, one -e per command
sed -n -e '/^Host/{' -e 's/Host/H/' -e 'p' -e '}' config

Nesting works, and each level may carry its own address:

sed -n '/^\[section\]/,/^\[/{
  /^key/{
    s/=.*/=newvalue/
    p
  }
}' config.ini

Written across real newlines like that, the terminator issue disappears, which is why anything non-trivial belongs in a -f script file.

Address cheat sheet by command

CommandAddresses accepted
= a i r q Q Fone (GNU allows two for a i r)
s y d D p P l n N h H g G x w W z czero, one, or two
b t T :: takes none, the branches take zero, one, or two
{zero, one, or two

Q T W z F R are GNU-only commands. macOS sed answers invalid command code for each of them.

sed '2q'  file        # print 2 lines and stop, like head -2
sed '1,3d' file       # two addresses on d
sed '2,4c\
REPLACED' file        # c on a range collapses it to one line