Remo.D
Remo.D

Reputation: 16512

sed: joining lines depending on the second one

I have a file that, occasionally, has split lines. The split is signaled by the fact that the line starts with '+' (possibly preceeded by spaces).

line 1
line 2
  + continue 2
line 3
...

I'd like join the split line back:

line 1
line 2 continue 2
line 3
...

using sed. I'm not clear how to join a line with the preceeding one.

Any suggestion?

Upvotes: 22

Views: 12119

Answers (6)

potong
potong

Reputation: 58371

This might work for you:

sed 'N;s/\n\s*+//;P;D' file

These are actually four commands:

  • N
    Append line from the input file to the pattern space
  • s/\n\s*+//
    Remove newline, following whitespace and the plus
  • P
    print line from the pattern space until the first newline
  • D
    delete line from the pattern space until the first newline, e.g. the part which was just printed

The relevant manual page parts are

To add 1 or more + lines, use:

sed ':a;N;s/\n\s*+//;ta;P;D' file

Upvotes: 29

stevesliva
stevesliva

Reputation: 5655

Different use of hold space with POSIX sed... to load the entire file into the hold space before merging lines.

sed -n '1x;1!H;${g;s/\n\s*+//g;p}'

  • 1x on the first line, swap the line into the empty hold space
  • 1!H on non-first lines, append to the hold space
  • $ on the last line:
    • g get the hold space (the entire file)
    • s/\n\s*+//g replace newlines preceeding +
    • p print everything

Input:

line 1
line 2
  + continue 2
  + continue 2 even more
line 3
+ continued

becomes

line 1
line 2 continue 2 continue 2 even more
line 3 continued

This (or potong's answer) might be more interesting than a sed -z implementation if other commands were desired for other manipulations of the data you can simply stick them in before 1!H, while sed -z is immediately loading the entire file into the pattern space. That means you aren't manipulating single lines at any point. Same for perl -0777.

In other words, if you want to also eliminate comment lines starting with *, add in /^\s*\*/d to delete the line

sed -n '1x;/^\s*\*/d;1!H;${g;s/\n\s*+//g;p}'

versus:

sed -z 's/\n\s*+//g;s/\n\s*\*[^\n]*\n/\n/g'

The former's accumulation in the hold space line by line keeps you in classic sed line processing land, while the latter's sed -z dumps you into what could be some painful substring regexes.

But that's sort of an edge case, and you could always just pipe sed -z back into sed. So +1 for that.

Footnote for internet searches: This is SPICE netlist syntax.

Upvotes: 4

xebeche
xebeche

Reputation: 975

A solution for versions of sed that can read NUL separated data, like here GNU Sed's -z:

sed -z 's/\n\s*+//g'

Compared to potong's solution this has the advantage of being able to join multiple lines that start with +. For example:

line 1
line 2
  + continue 2
  + continue 2 even more
line 3

becomes

line 1
line 2 continue 2 continue 2 even more
line 3

Upvotes: 2

user6307369
user6307369

Reputation:

You can use Vim in Ex mode:

ex -sc g/+/-j -cx file
  1. g global search

  2. - select previous line

  3. j join with next line

  4. x save and close

Upvotes: 3

glenn jackman
glenn jackman

Reputation: 246764

I'm not partial to sed so this was a nice challenge for me.

sed -n '1{h;n};/^ *+ */{s// /;H;n};{x;s/\n//g;p};${x;p}'

In awk this is approximately:

awk '
    NR == 1 {hold = $0; next}
    /^ *\+/ {$1 = ""; hold=hold $0; next}
    {print hold; hold = $0}
    END {if (hold) print hold}
'

If the last line is a "+" line, the sed version will print a trailing blank line. Couldn't figure out how to suppress it.

Upvotes: 3

William Pursell
William Pursell

Reputation: 212228

Doing this in sed is certainly a good exercise, but it's pretty trivial in perl:

perl -0777 -pe 's/\n\s*\+//g' input

Upvotes: 4

Related Questions