Joining continuation lines or editing a line based on its neighbour needs sed's multi-line commands.
Normally sed sees one line at a time. A few commands widen the view:
Nappends the next input line to the pattern space, separated by\n.Pprints up to the first newline;Ddeletes up to the first newline and restarts the cycle without reading a new line.N;P;Dis the sliding two-line window.- The hold space is a second buffer that persists between lines.
hcopies the pattern space into it,Happends,gcopies it back,Gappends it to the pattern space,xswaps the two. :labeldefines a label,b labelbranches to it, andt labelbranches only if a substitution succeeded since the last input line ort.
Common uses:
sed ':a; /\\$/ { N; s/\\\n//; ba }'joins lines ending in a backslash with the next.sed -n '1!G; h; $p'reverses a file (whattacdoes).- GNU sed's
-ztreats the whole input as NUL-separated, so a file without NULs becomes one big "line" that you can edit with regexes containing\n.
This is where sed programs become hard to read. If the logic needs more than a couple of these commands, an awk script, which has real variables and if, is usually clearer and easier to maintain.
Example
# join lines ending in a backslash with the next line
printf 'cmd --a \\\n --b\nnext\n' | sed -e ':a' -e '/\\$/{N; s/\\\n *//; ba' -e '}'
# cmd --a --b
# next
# reverse a file: prepend each line to the hold space, print at the end
printf '1\n2\n3\n' | sed -n '1!G; h; $p' # 3 / 2 / 1
# print each line that comes right before a line containing ERROR
printf 'a\nb\nERROR\nc\n' | sed -n '$!N; /\nERROR/P; D' # b
# -z: replace the newline between two lines
printf 'key=\nvalue\n' | sed -z 's/=\n/=/' # key=valueRemember
- N appends the next line; P and D work on the first line of the buffer
- h/H/g/G/x move text between the pattern space and the hold space
- Switch to awk once a sed program needs more than a few of these
Watch out for
- Forgetting that N on the last line prints and exits in GNU sed but behaves differently elsewhere; guard with $!N.