61

I have a file that contains:

something



something else

something else again

I need a bash command, sed/grep w.e that will produce the following output

something

something else

something else again

In other words, I need to replace multiple blank lines with just a single blank line. grep/sed are line based. I've never found a BASH solution that would work on multi-line regex patterns.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Nick Zalutskiy
  • 14,952
  • 7
  • 53
  • 50

14 Answers14

121

For BSD-derived systems (including GNU):

You just need cat with the -s option which causes it to remove repeated empty lines from its output:

cat -s

From man page: -s --squeeze-blank: suppress repeated empty output lines.

o11c
  • 15,265
  • 4
  • 50
  • 75
marco
  • 4,455
  • 1
  • 23
  • 20
40

I just solved this problem by sed. Even if this is a 7 year old question, someone may find this helpful, so I am writing my solution by sed here:

sed 'N;/^\n$/D;P;D;'
Dana Robinson
  • 4,304
  • 8
  • 33
  • 41
WKPlus
  • 6,955
  • 2
  • 35
  • 53
17
grep -A1 . <yourfile> | grep -v "^--$"

This grep solution works assuming you want the following:

Input

line1

line2
line3


line4



line5

Output

line1

line2
line3

line4

line5
heemayl
  • 39,294
  • 7
  • 70
  • 76
biggusjimmus
  • 2,706
  • 3
  • 26
  • 31
  • I like it, very elegant solution – duckyflip May 29 '09 at 00:24
  • 5
    Frickin' brilliant, actually. I wouldn't have come up with this in a million years. Nice work. A _slightly_ more robust version goes like this of course (to handle non-empty blank lines): grep -v -A1 '^[[:blank:]]*$' | grep -v '^--$' – Dan Moulding May 29 '09 at 03:43
13

Actually, if you replace multiple newlines with a single newline, the output would be:

something
something else
something else again

You can achieve this by:

sed /^$/d FILE
Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
  • This is correct, obviously :) What OP likely meant is to replace multiple "blank" (note that this is not necessarily empty, since lines can have invisible white space) with one empty line (which unless it is at the beginning or end of the sequence) means two newline chars. – arielf Jun 25 '16 at 20:59
4

If someone want use perl

perl -00pe0 < file

will do the same, as cat -s :)

clt60
  • 62,119
  • 17
  • 107
  • 194
4

Use awk:

awk '{ /^\s*$/?b++:b=0; if (b<=1) print }' file

Breakdown:

/^\s*$/?b++:b=0
    - ? :       the ternary operator
    - /^\s*$/   matches a blank line
    - b         variable that counts consecutive blank lines (b++).
                however, if the current line is non-blank, b is reset to 0.


if (b<=1) print
    print if the current line is non-blank (b==0)
          or if there is only one blank line (b==1).

By adjusting the regex, you can generalize it to other scenarios like squeezing multiple blank lines (">") in email: https://stackoverflow.com/a/59189823/12483961

Shaoyun
  • 91
  • 4
  • 1
    I really like this, because it is the first solution that works on DOS files (with \r\n newlines) and because you can change the b<=1 to, say, b<=2 to allow up to 2 blank lines. Btw you can replace the file inline with: awk -i inplace – JohnFlux Sep 30 '21 at 22:40
4

A solution with awk, which replaces several blank lines with a single blank line:

awk 'BEGIN{bl=0}/^$/{bl++;if(bl==1)print;else next}/^..*$/{bl=0;print}' myfile
mouviciel
  • 66,855
  • 13
  • 106
  • 140
4

Usually, if I find that sed can't do something I need, I turn to awk:

awk '
BEGIN {
    blank = 0;
}

/^[[:blank:]]*$/ {
     if (!blank) {
          print;
     }
     blank = 1;
     next;
}

{
     print;
     blank = 0;
}' file
Dan Moulding
  • 211,373
  • 23
  • 97
  • 98
3

This uses marco's solution on multiple files:

for i in *; do FILE=$(cat -s "$i"); echo "$FILE" > "$i"; done
Community
  • 1
  • 1
2

Python, with regular expression:

import re
import sys
sys.stdout.write(re.sub('\n{2,}','\n\n', sys.stdin.read()))
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
2

Use python:

s = file("filename.txt").read()
while "\n\n\n" in s: s = s.replace("\n\n\n", "\n\n")
import sys
sys.stdout.write(s)
Jonas Kölker
  • 7,680
  • 3
  • 44
  • 51
0

Super easy to do with vim. Just open the file and type the following:

:%s/\n\n\n*/\r\r/

That will reduce all blocks of more than 2 new lines to 2 new lines. Hope this helps!

Carl Sverre
  • 1,139
  • 10
  • 19
-3

I take it that you'll probably want to remove lines that only have whitespace.

That can be done with:

sed /^[:space:]*$/d FILE
Clue Less
  • 3,147
  • 2
  • 20
  • 10
-6

Pipelining it to |uniq may be solution (if other than empty lines don't duplicate)

mateusza
  • 5,341
  • 2
  • 24
  • 20