Juan
Juan

Reputation: 1371

Join current and next line, then the next line and its successor using sed

Given the input:

1234
5678
9abc
defg
hijk

I'd like the output:

12345678
56789abc
9abcdefg
defghijk

There are lots of examples using sed(1) to joining a pair of lines, then the next pair after that pair and so on. But I haven't found an example that joins lines 1 with 2, 2 with 3, 3 with 4, ...

sed(1) solution preferred. Other options are less interesting - e.g., awk(1), python(1) and perl(1) implementations are fairly easy. I'm specifically stumped on a successful sed(1) incantation.

Upvotes: 1

Views: 218

Answers (3)

Benjamin W.
Benjamin W.

Reputation: 52191

This does it (now improved, thanks to potong's hint):

$ sed -n 'N;s/\n\(.*\)/\1&/;P;D' infile
12345678
56789abc
9abcdefg
defghijk

In detail:

N                 # Append next line to pattern space
s/\n\(.*\)/\1&/   # Make 111\n222 into 111222\n222
P                 # Print up to first newline
D                 # Delete up to first newline

The substitution makes these two lines

1111
2222

which in the pattern space look like 1111\n2222 into

11112222
2222

and the P and D print/delete the first line from the pattern space.

Notice that we never hit the bottom of the script (D starts a new loop) until the very last line, where N can't fetch a new line and would just print the last line on its own, if we didn't suppress that with -n.

Upvotes: 3

aragaer
aragaer

Reputation: 17858

sed '1h;1d;x;G;s/\n//'

I guess it can be done some other way, but this works for me:

$ cat in
1234
5678
9abc
defg
hijk
$ sed '1h;1d;x;G;s/\n//' in
12345678
56789abc
9abcdefg
defghijk

How it works: we put first line to hold space and that's it for first line. Every line after the first - swap it with hold space, append the new hold space to the old hold space, remove newline.

Upvotes: 4

Juan
Juan

Reputation: 1371

Tweaking another answer (full credit to @aragaer) to handle single line input (and be more portable to bsd sed as well as gnu sed than the original version - update: that answer has been edited another way for portability):

% cat >> inputfile << eof
12
34
56
eof

% sed -e '1{$p;h;d' -e '}' -e 'x;G;s/\n//' inputfile  # bsd + gnu sed [1]
1234
3456

or

% cat joinsuccessive.sed
1{
 $p;h;d
}
x;G;s/\n//
% sed -f joinsuccessive.sed inputfile
1234
3456

Here's an annotated version.

1{                # special case for first line only:
 $p               #  even MORE special case: print current line for input with
                  #  only a single line
 h                # add line 1 to hold space (for joining with successive lines)
 d                # delete pattern space and move to next line (without printing)
}
x                 # for lines 2+, swap pattern space (current line) and hold space
G                 # add newline + hold space (now has current line) to pattern space
                  # (previous line) giving prev line, newline, curr line in pattern
                  # space (and curr line is in hold space)
s/\n//            # remove newline added by G (between lines) before printing the
                  # pattern space

[1] bsd sed(1) wants a closing brace to be on a line by itself. Use -e to "build" the script or put the commands in a sed script file (and use -f joinsuccessive.sed).

Upvotes: 0

Related Questions