2

How can sed be used to add a \n to the beginning and to the end of every line matching the pattern %%###? This is how far I got:

If foo.txt contains

foobar
%%## Foo
%%### Bar
foobar

then sed 's/^%%###/\n&\n/g' foo.txt gives

foobar
%%## Foo

%%###
 Bar
foobar

instead of

foobar
%%## Foo

%%### Bar

foobar

Note: This seems related to this post

Update: I'm actually looking for case where lines starting with the pattern are considered only.

Marius Hofert
  • 6,546
  • 10
  • 48
  • 102

4 Answers4

1

It is cumbersome to directly add newlines via sed. But here is one option if you have perl available:

$ foo.txt | perl -pe 's/(.*%%###.*)/\n$1\n/'

Here we capture every matching line, which is defined as any line containing the pattern %%### anywhere, and then we replace with that line surrounded by leading and trailing newlines.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • How is that less cumbersome than [the sed solution](https://stackoverflow.com/a/47853333/1745001)? – Ed Morton Dec 17 '17 at 15:22
  • @EdMorton I read in a couple of places that generating newlines from SED had caveats. This answer seemed reasonable. – Tim Biegeleisen Dec 17 '17 at 15:22
  • The only caveat is that some seds won't accept `\n` as a newline and you'd need backslash followed by a literal newline character or to rely on the shell to expand `$'\n'` but if you have or can install perl then you have or can install a sed that accepts `\n`. – Ed Morton Dec 17 '17 at 15:24
1

With GNU sed:

sed 's/.*%%###.*/\n&\n/' file

Output:

foobar
%%## Foo

%%### Bar

foobar

&: Refer to that portion of the pattern space which matched

If you want to edit your file "in place" use sed's option -i.

Cyrus
  • 84,225
  • 14
  • 89
  • 153
  • Hi, thanks. Do you have a version which only matches lines *starting* with the pattern? – Marius Hofert Dec 17 '17 at 08:24
  • Replace first `.*` with `^`. – Cyrus Dec 17 '17 at 08:35
  • @MariusHofert If the input contained 2 consecutive `%%###` lines that would output 2 blank lines between them. Is that really what you want? It'd also add blank lines even if the surrounding lines were already blank. Something to consider... – Ed Morton Dec 17 '17 at 15:31
1

This might work for you (GNU sed):

sed '/%%###/!b;G;i\\' file

For those lines that meet the criteria, append a newline from the hold space (the hold space by default contains a newline) and insert an empty line.

Another way:

sed -e '/%%###/!b;i\\' -e 'a\\' file

This time insert and then append empty lines.

N.B. The i and a must be followed by a newline, this can be achieved by putting them in separate -e invocations.

A third way:

sed '/%%###/!b;G;s/.*\(.\)/\1&/' file

As in the first way, append a newline from the hold space, then copy it i.e. the last character of the amended current line, and prepend it to the current line.

Yet another way:

sed '/%%###/{x;p;x;G}' file

Swap to the hold space, print the newline, swap back and append the newline.

N.B. If the hold space may not be empty (a previous, x,h,H,g or G command may have changed it) the buffer may be cleared before it is printed (p) by using the zap command z.

And of course:

sed  '/%%###/s/^\|$/\n/g' file
potong
  • 55,640
  • 6
  • 51
  • 83
0

Personally I'd use a string instead of regexp comparison since there's no repexp characters in the text you want to match and if there were you wouldn't want them treated as such, and just print newlines around the string instead of modifying the string itself:

awk '{print ($1=="^%%###" ? ORS $0 ORS : $0)}' file

The above will work with any awk in every shell on every UNIX box and is easily modified if you don't want multiple blank lines between consecutive %%### lines or don't want blank lines added if the existing surrounding lines are already blank or you need to do anything else.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185