5

I need to add a new line before any line containing a pattern where we can assume that the pattern is always the first string of the current line. For example

This is a
pattern
This is a
pattern

I can add a new line with the sed command

sed -i 's/pattern\+/\n&/g' file

to get the output

This is a

pattern
This is a

pattern

To prevent multiple new lines being added (in case of multiple execution) I want to check whether the line before the pattern is empty. I know I can do that with

if [ "$line" == "" ]; then

But how do I determine the previous line, of a matching pattern, in the first place?

EDIT: Pattern can occur multiple times.

  • That's right. And yes it is possible that the pattern can occur multiple times in a file. After looking into the answer from @maulinglawns it's probably better to use another language because grep is only applied to the first found line – Cram Nov 14 '16 at 14:31
6

You could store the previous line in the hold space:

sed '
 /^pattern/{
   x
   /./ {
     x
     s/^/\
/
     x
   }
   x
 }
 h'

It would be more legible with awk though:

awk '!previous_empty && /pattern/ {print ""}
     {previous_empty = $0 == ""; print}'

Like the GNU implementations of sed has a -i option for in-place editing, the GNU implementation of awk as -i inplace for that.

  • Thank you, this does the trick! I will use awk for that since it is (in this case) more understandable for other users. – Cram Nov 14 '16 at 14:47
3

But how do I determine the previous line, of a matching pattern, in the first place?

Umm... perhaps this will work.

Using grep and the -B switch:

 -B num, --before-context=num
         Print num lines of leading context before each match.  See
         also the -A and -C options.

Consider this infile:

cat infile 
foo

bar
baz

Now, if I grep for bar, the previous line should be empty:

if [[ $(grep -B 1 'bar' infile | head -1) == "" ]]; then echo "line is empty"; fi
line is empty

As opposed to using grep on baz, where the previous line is not empty:

if [[ $(grep -B 1 'baz' infile | head -1) == "" ]]; then echo "line is empty"; fi
<no output>
  • What if you have multiple lines that match the pattern ? – don_crissti Nov 14 '16 at 14:13
  • Then I would use Python! – maulinglawns Nov 14 '16 at 14:14
3

Another way with sed:

sed -e 'tD' -e '$!N;/.\npattern/s/\n/&&/;:D' -e 'P;D' infile

This was explained in detail here: it's basically an N;P;D cycle where we account for the newlines we edit in, so each time the script inserts a \newline it executes only P and D without N so as to always have only two lines in the pattern space.

  • 1
    Though this solution will not insert a newline before the first line of input if it matches... which may or may not be what you want. To always insert add -e '1{/pattern/{x;p;x;};}' before -e 'tD' – don_crissti Nov 14 '16 at 15:30
3

I know your question was initially about sed, but there is a beautifully simple answer in vim:

:g/.\npattern/norm o

Or, if you would rather run this entirely from the command line:

vim file -c "g/.\npattern/norm o" -c "wq"

The way it works, is that it looks for any line that matches the following regex:

.\npattern

which is any non-empty line followed by your pattern. Then, for each match, it applies the following command norm o, which opens up a newline below the current cursor location.

  • I can't find any mention in POSIX ex or POSIX vi of multi-line regexes; pretty sure this is Vim-specific. It may be worth noting that in Vim, . matches any non-newline character, whereas \_. matches any character including a newline. (See :help /\_. from within Vim.) – Wildcard Nov 14 '16 at 23:56
2

If we have gnused (the default in Linux and many others, and available for all)

sed -zri  's/([^\n]\n)(pattern)/\1\n\2/g' file

where

  • ([^\n]\n)(pattern) the pattern after a non-empty line
  • -z separate "lines" by the null char (slurp the file)
  • -r to have extended regular expressions
1

Using ex, POSIX specified features only

printf '%s\n' 0a '' . 1d 'g/pattern/-put | -,.!uniq' x | ex file

Quick summary of the commands passed to ex by printf:

0a - append after line 0
   - an empty line
.  - stop appending
1d - delete line 1 (the new empty line) into the unnamed register (a.k.a. buffer)

g/pattern/-put | -,.!uniq

g/pattern/ - for every line in the file matching "pattern"
- - on the *previous* line,
put - "put" (linewise append) the contents of the unnamed register
| - and also do the following (still part of the g// command)
-,. - take the previous and current lines
!uniq - and run them through the external command "uniq"
        (replacing the lines with the output)

x - save changes and exit

ex is worth learning. :)

0

Suppose we reframe the requirements like this:

  • We have a file of paragraphs, which are regions of consecutive non-empty lines, separated by one or more empty lines.
  • Whenever "pattern" occurs inside a paragraph, we would like that line to start a new paragraph, except when "pattern" occurs in the first line of a paragraph.

  • Furthermore, we do not mind normalizing the file so that there is exactly one empty line between the paragraphs, and no stray empty lines at the beginning or end.*

If these requirements are acceptable, we can take advantage of Awk paragraph mode (enabled an empty value in RS):

awk 'BEGIN { RS=""; FS="\n" }
           { print sep $1;
             for (i = 2; i <= NF; i++)
             { if ($i ~ /pat/) print ""; print $i }
             sep=FS }'

In paragraph mode, the records are paragraphs. Because we are using \n as FS, the fields $1, $2, ... $NF correspond to lines of paragraphs. For instance, if NF is 5, then we are dealing with a five line paragraph. The paragraph-separating newlines are removed and each record $0 contains only the interior newlines between the lines, and the field splitting is done on these.

A paragraph has at least one line, because paragraphs cannot be empty: an empty paragraph looks like two consecutive newlines which are part of the same paragraph separating sequence.

So, without checking that NF is at least 1, we just print the first paragraph line with print sep $1. The first time around, sep is empty so is of no consequence; but after the first paragraph, we set sep to a newline, so that the next print sep $1 will generate the paragraph separation.

After printing the first line, we iterate over the remaining lines, if any, and print them. Here, we check whether each line matches the pattern. If so, we issue an extra blank line before printing it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.