Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

grep (awk) a file from A to first empty line

Tags:

grep

bash

awk

I need to grep a file from a line containing Pattern A to a first empty line. I used awk but I don't know how to code this empty line.

cat ${file} | awk '/Pattern A/,/Pattern B/'
like image 627
herder Avatar asked May 23 '14 14:05

herder


4 Answers

sed might be best:

sed -n '/PATTERN/,/^$/p' file

To avoid printing the empty line:

sed -n '/PATTERN/,/^$/{/^$/d; p}' file

or even better - thanks jthill!:

sed -n '/PATTERN/,/^$/{/./p}' file

Above solutions will give more output than needed if PATTERN appears more than once. For that, it is best to quit after empty line is found, as jaypal's answer suggests:

sed -n '/PATTERN/,/^$/{/^$/q; p}' file

Explanation

  • ^$ matches empty lines, because ^ stands for beginning of line and $ for end of line. So that, ^$ means: lines not containing anything in between beginning and end of line.
  • /PATTERN/,/^$/{/^$/d; p}
    • /PATTERN/,/^$/ match lines from PATTERN to empty line.
    • {/^$/d; p} remove (d) the lines being on ^$ format, print (p) the rest.
  • {/./p} just prints those lines having at least one character.

With awk you can use:

awk '!NF{f=0} /PATTERN/ {f=1} f' file

Same as sed, if it has many lines with PATTERN it would fail. For this, let's exit once empty line is found:

awk 'f && !NF{exit} /PATTERN/ {f=1} f' file

Explanation

  • !NF{f=0} if there are no fields (that is, line is empty), unset the flag f.
  • /PATTERN/ {f=1} if PATTERN is found, set the flag f.
  • f if flag f is set, this is True, so it performs the default awk behaviour: print the line.

Test

$ cat a
aa
bb
hello
aaaaaa
bbb

ttt

$ awk '!NF{f=0} /hello/ {f=1} f' a
hello
aaaaaa
bbb
$ sed -n '/hello/,/^$/{/./p}' a
hello
aaaaaa
bbb
like image 159
fedorqui 'SO stop harming' Avatar answered Oct 27 '22 04:10

fedorqui 'SO stop harming'


Using sed:

sed -n '/PATTERN/,/^$/{/^$/q;p;}' file

Using regex range, you define your range from the PATTERN to blank line (/^$/). When you encounter a blank line, you quit else you keep printing.

Using awk:

awk '/PATTERN/{p=1}/^$/&&p{exit}p' file 

You enable a flag when you encounter your PATTERN. When you reach a blank line and flag is enabled, you exit. If not, you keep printing.

Another alternate suggested by devnull in the comments is to use pcregrep:

pcregrep -M 'PATTERN(.|\n)*?(?=\n\n)' file
like image 35
jaypal singh Avatar answered Oct 27 '22 06:10

jaypal singh


I think this is a nice, readable Perl one-liner:

perl -wne '$f=1 if /Pattern A/; exit if /^\s*$/; print if $f' file
  • Set the flag $f when the pattern is matched
  • Exit if a blank line (only whitespace between start and end of line) is found
  • Print the line if the flag is set

Testing it out:

$ cat file
1
2
Pattern A
3
4
5
6

7
8
9

$ perl -wne '$f=1 if /Pattern A/; exit if /^$/; print if $f' file
Pattern A
3
4
5
6

Alternatively, based on the suggestion by @jaypal, you could do this:

perl -lne '/Pattern A/ .. 1 and !/^$/ ? print : exit' file

Rather than using a flag $f, the range operator .. takes care of this for you. It evaluates to true when "Pattern A" is found on the line and remains true indefinitely. When it is true, the other part will be evaluated and will print until a blank line is found.

like image 3
Tom Fenech Avatar answered Oct 27 '22 04:10

Tom Fenech


Never use

/foo/,/bar/

in awk unless you want to get from the first occurrence of "foo" to the last occurrence of "bar" as it makes trivial jobs marginally briefer but even slightly more interesting requirements require a complete re-write.

Just use:

/foo/{f=1} f{print; if (/bar/) f=0}

or similar instead.

In the case the awk solution is:

awk '/pattern/{f=1} f{print; if (!NF) exit}' file
like image 2
Ed Morton Avatar answered Oct 27 '22 05:10

Ed Morton