Searching for pattern and then counting each blank line after the pattern until the 3rd blank line is found; then I want to place a comma and a semicolon in that blank line. There could be lots of data in between the blank lines, or only one line of text in between the blank lines.


Grouping Cat

this is the 3rd blank line

I have tried the following without success. Any suggestions?

sed '/GROUP DIRECTORY CATEGORIES/,/^$/^$/^$/,;/' file1>file2
sed '/GROUP DIRECTORY CATEGORIES/$/$/$/,;/' file1>file2
sed '/GROUP DIRECTORY CATEGORIES/{$;$;$/./,;/1;}' file1>file2

While sed can be used for this, anything that involves counting is usually easier done with awk.

Let's consider this test file:

$ cat file1

Grouping Cat


Above is 3rd blank line

To add a ,; to the third blank line after GROUP DIRECTORY CATEGORIES:

$ awk '/GROUP DIRECTORY CATEGORIES/{f=1}  f && /^$/ {f++; if (f==4) $0=",;"} 1' file1

Grouping Cat

Above is 3rd blank line

How it works:


    Anytime that a line is found which matches the regex GROUP DIRECTORY CATEGORIES, variable f is set to 1.

  • f && /^$/ {f++; if (f==4) $0=",;"}

    If f is non-zero and the current line is blank, /^$/, then increment f by one. If f is 4, then replace the current blank line with ,;.

  • 1

    1 is awk's cryptic shorthand for print-the-line.

  • This is a great solution. Thanks for the explanation of it in the way that you broke down all the parts. Very helpful! – 985ranch Sep 2 '16 at 20:52

Fundamentally, your approach fails because your expression is only reading a single line into its pattern space each time - so it can never match multiple blank lines (even if you could construct an appropriate regex).

However it is possible to read multiple lines into pattern space using the N command in a loop. With the possible exception of GNU sed's multiline modifier, newlines are represented in the multiline pattern space as \n sequences - so to test for blank lines you will need to use \n\n rather than ^$. So for example:

sed -E '
  :a                            # label the start of a loop
  $!N                            # if not at the end of file, read & append the next line
  s/((\n\n.*){2})\n\n/\1\n,;\n/ # match and capture 2 instances of successive newlines, 
                                # followed by a 3rd; if found, replace the 3rd by \n,;\n 
  t                             # branch out of the loop on successful replacement
  ba                            # else branch back to label 'a'
  }' file1

In GNU sed, the t;ba sequence may be replaced by Ta.

  • Interesting approach. SED can be AWKward I guess. I will consider trying it next time. – 985ranch Sep 2 '16 at 20:55

This sort of complex addressing requirement is where ex (or vi) really shines.

ex is a POSIX-specified tool that is the predecessor of vi (the "visual editor.") It's notable that all ex-style commands remain usable in vi as well as in Vim.

In vi or Vim, assuming you want the first instance of /pattern/ from the beginning of the document, you can do this as follows:

  • Open the file on the command line by typing vi file.txt and pressing <Enter>

  • Type the following, including the leading colon. Then press <Enter>.

    :0/GROUP DIRECTORY CATEGORIES//^$//^$//^$/s/^/,;/
  • Save the changes by typing :x and pressing <Enter>.

In the above command, everything up to the lowercase s is just an address. The s command should be pretty obvious.

The address means, "From the very start of the file (line '0'), the first instance forward of 'GROUP DIRECTORY CATEGORIES', and from there the first instance forward of a blank line, then the next instance of a blank line, then the next, then run the substitute command."

There are many other ways to split this up; it doesn't have to be a one liner.

You could also accomplish this edit with the following ex commands:


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.