0

I have the below text

Start
bla
bla
bla
end
Start
bla
bla
MATCH
bla
end
Start
bla
bla
bla
end

i will need just the below

Start
bla
bla
MATCH
bla
end

to put it in English. Print lines between two patterns which includes a match string in between.

tried

awk '/Start /,/End/' file

perl -lne 'print if  /start/ .. /end/' file

was not able to grep a match inbetween the tags

need a one liner command preferrably

TLP
  • 66,756
  • 10
  • 92
  • 149
peepu86
  • 3
  • 1
  • 3
    `/start/` does not match `Start`. And `/End/` does not match `end`. – TLP Sep 02 '15 at 00:12
  • 1
    A similar question is answered here: http://stackoverflow.com/questions/17988756/how-to-select-lines-between-two-marker-patterns-which-may-occur-multiple-times-w – Atri Sep 02 '15 at 00:18
  • Make regex case insesitive, `perl -ne 'print if /start/i .. /end/i' file` – mpapec Sep 02 '15 at 08:14

4 Answers4

1

Set the input record separator ($/) to end\n, and then a regex check for MATCH will check each section between Start and end\n as if it's a single line.

$ perl -e '$/="end\n"; for (<>){print if /MATCH/;}' in.txt
Start
bla
bla
MATCH
bla
end
stevieb
  • 9,065
  • 3
  • 26
  • 36
  • Note that you may have to put anchors/boundaries around `MATCH` depending on what `MATCH` really is. – stevieb Sep 02 '15 at 00:54
1

The problem you've got is that your range operator still works line by line.

E.g.

while ( <> ) {
    print if m/start/i .. m/end/i; 
}

Is still using the record separator as \n - each iteration of the loop will read another line from the file, but you won't be able to match the whole chunk ... because it may not have read ahead that far yet.

You could do this via regular expression matching 'Start .. end' chunks:

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

my @chunks = do { local $/; <DATA> =~ m/Start.*?end/mgs };
print grep { m/MATCH/ } @chunks;

__DATA__
Start
bla
bla
bla
end
Start
bla
bla
MATCH
bla
end
Start
bla
bla
bla
end

Or as another poster notes - set $/ to 'end'. This has a slight drawback, in that it'll ignore 'Start', which means you might get additional content if you don't have the two matched properly.

You might also try:

local $/ = "end\nStart"; 

Which will split your data properly, but again - might not handle all the scenarios correctly.

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

local $/ = "end\nStart";

while ( <DATA> ) {
    chomp; 
    print "Chunk: $_\n";
    print "----\n";
    print "Matches!\n" if m/MATCH/;
}

These can one-liner as:

perl -lne 'BEGIN { $/ = "end\nStart" } print if /MATCH/' file
Sobrique
  • 52,974
  • 7
  • 60
  • 101
0

awk to the rescue

awk 'BEGIN {ORS=RS="end\n"} /MATCH/'

set the record separator as defined and find the records which matches. If multiple matches not required, this will only print the first match.

awk 'BEGIN {ORS=RS="end\n"} /MATCH/{print;exit}'
karakfa
  • 66,216
  • 7
  • 41
  • 56
0

Given:

$ echo "$tgt" 
Start
bla
bla
bla
end
Start
bla
bla
MATCH
bla
end
Start
bla
bla
bla
end

You can 'slurp' the file and then test each match group for MATCH:

$ echo "$tgt" | perl  -0777 -lne 'for (/^Start.*?^end/msg) { print if /^MATCH/m; }'
Start
bla
bla
MATCH
bla
end

Which works for a file as well.

dawg
  • 98,345
  • 23
  • 131
  • 206