Find and select complete paragraph

HI Friends,
I need your help once again.

I have following

$cat april_2015.txt
== :: ==
Gender: Female
Service: Tattoo
Nature: Permanent
Amt: 21000 INR
Date: 04/04/2015
Artist: Anushka
== :: ==
Gender: Female
Service: Makeup
Nature: Bridal
Amt: 19200 INR
Date: 05/04/2015
Artist: Jenn
== :: ==
Gender: Male
Service: Tattoo
Nature: Permanent
Amt: 9500 INR
Date: 05/04/2015
Artist: Anushka
== :: ==
$
$

I want to grep "Permanent" in such a way that It will select it as follows.

== :: ==
Gender: Female
Service: Tattoo
Nature: Permanent
Amt: 21000 INR
Date: 04/04/2015
Artist: Anushka
== :: ==
Gender: Male
Service: Tattoo
Nature: Permanent
Amt: 9500 INR
Date: 05/04/2015
Artist: Anushka
== :: ==

Kindly suggest.
Anu...

Hi Anu,

What have your tried to solved this problem?

THank you for the suggestion.

I can not think of any solution for this selection criteria.
Kindly help.

Given that you have asked us for help 61 times before with some of your problems being very similar to this one, it is disappointing that you are unable to guess that awk would be a good way to solve a problem like this and show us a start to a solution to this on your own.

You might try something like:

awk '
$0 == "== :: ==" {
	if(perm) {
		if(!pc++)
			print
		for(i = 1; i <= lc; i++)
			print l
		print
		perm = 0
	}
	lc = 0
	next
}
{	l[++lc] = $0
	if($0 == "Nature: Permanent")
		perm = 1
}' april_2015.txt

As always, if you want to try this on a Solaris/SunOS system, change awk to /usr/xpg4/bin/awk or nawk .

This might not work on all awk versions:

awk '/Nat.*ermanent/ || NR==1; END {printf "\n"}' RS="== :: ==" ORS="== :: ==" file
== :: ==
Gender: Female
Service: Tattoo
Nature: Permanent
Amt: 21000 INR
Date: 04/04/2015
Artist: Anushka
== :: ==
Gender: Male
Service: Tattoo
Nature: Permanent
Amt: 9500 INR
Date: 05/04/2015
Artist: Anushka
== :: ==

On systems where it does work, this is a great solution. It doesn't work on all systems, and the standards state that the behavior is unspecified if RS is set to a string containing more than one character.

The standard doesn't place any limits like that on ORS ; only on RS .

Hi, just for fun, with grep (gnu version 2.16 or higher):

grep -Pzo '(?s)== :: ==(\n*[^=]\N)*Permanent(\n[^=]\N*)*|== :: ==(?=[\n]$)' file

Regards.

Got Perl?

#!/usr/bin/perl
use strict;
use warnings;

my $sep = $/ = "== :: ==\n";
my $pattern = "Permanent";
my $hat = 0;

while(<>) {
    if(/$pattern/){
        print $sep if $hat == 0 and ++$hat;
        print;
    }
}

Save as filter.pl
Run as perl filter.pl april_2015.txt
or
perl filter.pl april_2015.txt > permanent.txt
or
perl filter.pl april_2015.txt may_2015.txt june_2015.txt > permanent.txt
or
perl filter.pl *_2015.txt > permanent.txt

not much efficient by another approach.

awk ' BEGIN {printf "== :: =="; RS="==";FS="\n";OFS="\n" } $4 ~ "Permanent"{ printf $1 OFS $2 OFS $3 OFS $4 OFS $5 OFS $6 OFS $7 OFS "== :: =="} ' file

I tried code suggested by Aia and its working as per expectations.
However, I would like to run this perl in loop as I have around 830 different patterns like "Permanent".

I tried following

cat filter.pl
#!/usr/bin/perl
use strict;
use warnings;

my $sep = $/ = "== :: ==\n";
my $pattern = `$1`;
my $hat = 0;

while(<>) {
if(/$pattern/){
print $sep if $hat == 0 and ++$hat;
print;
 }
}

and made following loop.

for service in `cat input_patterns`
do
echo ${service}
perl filter.pl ${service} file>>pattern.out
done

however, its not working.
I am sure I am doing something wrong.
Kindly suggest.

The Perl program processes the data file to search for one pattern. Inside the loop, it runs as many times as there are patterns.
So, if your "input_patterns" file has 830 lines, then you process the data file 830 times, searching for one pattern each time!

To give you an analogy, let's say you want to go grocery shopping.
Do you do the following?
(1) Go to grocery store, buy eggs, come back.
(2) Then go to the same grocery store, buy milk, come back.
(3) Then go to the same grocery store, buy meat, come back.
(4) Then go to the same grocery store, buy drinks, come back.
...

I'm sure you see how inefficient this is, yet you're doing something similar in your code.

While this kind of code might work at a small scale (small data file, small pattern file), the inefficiency due to repeated scanning add up at a large scale.
Imagine searching for 10,000 patterns in a million line data file.
Do you want to scan a million line file 10,000 times, looking for one pattern each time?

This is not tested, however, if you want to use the Perl script in that way you need a different modification that what I highlighted in red.

#!/usr/bin/perl
use strict;
use warnings;

my $pattern = shift;
my $sep = $/ = "== :: ==\n";
my $hat = 0;


while(<>) {
    if(/$pattern/){
        print $sep if $hat == 0 and ++$hat;
        print;
    }
}

And then you can use the following shell script

#!/bin/bash

while read service; do
    perl filter.pl "$service" file.txt >> pattern.out
done < input_patterns

Of course, that might be slow due to all the times the binary perl gets called, and the opening, appending and closing of pattern.out. Your mileage may vary there.

Here's a Perl script that might work alone.
Again, it is not tested but you can try with just a portion of your 800 plus patterns input_patterns. It assumes one pattern per line.

#!/usr/bin/perl
use strict;
use warnings;

my $pattern;
my $pattern_source = shift;
{
    local $/ = undef;
    open my $fh, '<', $pattern_source or die "$!\n";
    $pattern = <$fh>;
    $pattern =~ s/\R(?!$)/\|/g;
    close $fh;
}
my $sep = $/ = "== :: ==\n";
my $hat = 0;
while(<>) {
    if(/$pattern/){
        print $sep if $hat == 0 and ++$hat;
        print;
    }
}

Use as perl filter.pl input_patterns april_2015.txt > pattern.out

Here's a Python script as well:

$ 
$ cat patterns.txt
Permanent
Geometric
$ 
$ cat april_2015.txt 
== :: ==
Gender: Female
Service: Tattoo
Nature: Permanent
Amt: 21000 INR
Date: 04/04/2015
Artist: Anushka
== :: ==
Gender: Female
Service: Makeup
Nature: Bridal
Amt: 19200 INR
Date: 05/04/2015
Artist: Jenn
== :: ==
Gender: Male
Service: Tattoo
Nature: Permanent
Amt: 9500 INR
Date: 05/04/2015
Artist: Anushka
== :: ==
Gender: Male
Service: Tattoo
Nature: Geometric
Amt: 9500 USD
Date: 05/04/2015
Artist: Kat Von D
== :: ==
$ 
$ cat -n process_files.py
     1	#!/usr/bin/env python
     2	from sys import argv
     3	# Accept file names as input parameters
     4	pattern_file = argv[1]
     5	data_file = argv[2]
     6	
     7	# Load patterns from pattern_file
     8	patterns = []
     9	with open(pattern_file, 'rt') as f:
    10	    for line in f:
    11	        line = line.replace('\n','')
    12	        patterns.append(line)
    13	
    14	# Read data_file; print data chunk if pattern was found
    15	chunk = []
    16	print_the_rest = 0
    17	with open(data_file, 'rt') as f:
    18	    for line in f:
    19	        line = line.replace('\n','')
    20	        if line == "== :: ==":
    21	            chunk = []
    22	            chunk.append(line)
    23	            print_the_rest = 0
    24	        else:
    25	            chunk.append(line)
    26	            param, value = line.split(': ')
    27	            if value in patterns:
    28	                for item in chunk:
    29	                    print item
    30	                chunk = []
    31	                print_the_rest = 1
    32	            elif print_the_rest:
    33	                print line
    34	
$ 
$ python process_files.py patterns.txt april_2015.txt
== :: ==
Gender: Female
Service: Tattoo
Nature: Permanent
Amt: 21000 INR
Date: 04/04/2015
Artist: Anushka
== :: ==
Gender: Male
Service: Tattoo
Nature: Permanent
Amt: 9500 INR
Date: 05/04/2015
Artist: Anushka
== :: ==
Gender: Male
Service: Tattoo
Nature: Geometric
Amt: 9500 USD
Date: 05/04/2015
Artist: Kat Von D
$ 
$ 

Dear Aia,

Thank you for your support.
The script is now written and have been sent to teating team.

Also, I am grateful to all the other friends who helped me here...
Thanks for the time and efforts.

Bye.
Anu.