Okay, I'll bite. In pure sed, we'll have to build the complete output in the hold buffer before printing it (because we see the stuff we want to print first last). A basic template can look like this:
sed 'N;G;h;$!d' filename # Incomplete!
That is:
N # fetch another line, append it to the one we already have in the pattern
# space
G # append the hold buffer to the pattern space.
h # save the result of that to the hold buffer
$!d # and unless the end of the input was reached, start over with the next
# line.
The hold buffer always contains the reversed version of the input processed so far, and the code takes two lines and glues them to the top of that. In the end, it is printed.
This has two problems:
- If the number of input lines is odd, it prints only the last line of the file, and
- we get a superfluous empty line at the end of the input.
The first is because N
bails out if no more lines exist in the output, which happens with an odd number of input lines; we can solve the problem by executing it conditionally only when the end of the input was not yet reached. Just like the $!d
above, this is done with $!N
, where $
is the end-of-input condition and !
inverts it.
The second is because at the very beginning, the hold buffer contains an empty line that G
appends to the pattern space when the code is run for the very first time. Since with $!N
we don't know if at that point the line counter is 1 or 2, we should inhibit it conditionally on both. This can be done with 1,2!G
, where 1,2
is a range spanning from line 1 to line 2, so that 1,2!G
will run G
if the line counter is not between 1 and 2.
The whole script then becomes
sed '$!N;1,2!G;h;$!d' filename
Another approach is to combine sed
with tac
, such as
tac filename | sed -r 'N; s/(.*)\n(.*)/\2\n\1/' # requires GNU sed
That is not the shortest possible way to use sed
here (you could also use tac filename | sed -n 'h;$!{n;G;};p'
), but perhaps easier to understand: Every time a new line is processed, N
fetches another line, and the s
command swaps them. Because tac
feeds us the lines in reverse, this restores pairs of lines to their original order.
The key difference to the first approach is the behavior for an odd number of lines: with the second approach, the first line of the file will be alone without a partner, whereas with the first it'll be the last.