grep a block of text delimited by two key lines
Solution 1
The following might work for you:
sed -n '/Beginning of block/!b;:a;/End of block/!{$!{N;ba}};{/some_pattern/p}' filename
Solution 2
I guess awk
is better for this:
awk '/Beginning of block/ {p=1};
{if (p==1) {a[NR]=$0}};
/some_pattern/ {f=1};
/End of block/ {p=0; if (f==1) {for (i in a) print a[i]};f=0; delete a}' file
Explanation
It just prints when the p
flag is "active" and some_pattern
is matched:
- When it finds
Beginning of block
, then makes variablep=1
and starts storing the lines in the arraya[]
. - If it finds
some_pattern
, it sets the flagf
to 1, so that we know the pattern has been found. - When it finds
End of block
it resetsp=0
. Ifsome_pattern
had been found since the lastBeginning of block
, all the lines that had been stored are printed. Finally a[] is cleared and f is reset; we will have a fresh start when we again encounterBeginning of block
.
Other test
$ cat a
Beginning of block
blabla
.........some_pattern.......
and here i am
hello
End of block
Beginning of block
...
... etc.
End of block
$ awk '/Beginning of block/ {p=1}; {if(p==1){a[NR]=$0}}; /some_pattern/ {f=1}; /End of block/ {p=0; if (f==1) {for (i in a) print a[i]}; delete a;f=0}' a
Beginning of block
blabla
.........some_pattern.......
and here i am
hello
End of block
Solution 3
Not sure if I missed something but here is a simpler variation of one of the answers above:
awk '/Beginning of block/ {p=1};
/End of block/ {p=0; print $0};
{if (p==1) print $0}'
You need to print the input line in the End of Block
case to get both delimiters.
I wanted a slight variation that doesn't print the delimiters. In the OP's question the delimiter pattern is simple and unique. Then the simplest is to pipe into | grep -v block
. My case was more irregular, so I used the variation below. Notice the next
statement so the opening block isn't printed by the third statement:
awk '/Beginning of block/ {p=1; next};
/End of block/ {p=0};
{if (p==1) print $0}'
Solution 4
Here's one way using awk
:
awk '/Beginning of block/ { r=""; f=1 } f { r = (r ? r ORS : "") $0 } /End of block/ { if (f && r ~ /some_pattern/) print r; f=0 }' file
Results:
Beginning of block
...
...
...
.........some_pattern.......
...
...
End of block
Related videos on Youtube
laurent
Updated on September 16, 2022Comments
-
laurent about 1 year
I have a text file that contains text blocks roughly formatted like this:
Beginning of block ... ... ... .........some_pattern....... ... ... End of block Beginning of block ... ... etc.
The blocks can have any number of lines but always start with the two delimiters. What I'd like to do is match "some_pattern" and print the whole block to stdout. With the example above, I would get this only:
Beginning of block ... ... ... .........some_pattern....... ... ... End of block
I've tried with something like this but without success:
grep "Beginning of block\n.*some_pattern.*\n.*End of block"
Any idea how to do this with grep? (or maybe with some other tool)
-
laurent almost 10 yearsThanks, but how can I do to retrieve only the block that contains "some_pattern"? I've updated my question to show the output I'm looking for. Any idea?
-
fedorqui almost 10 yearsUhms, that's more complicated. For this case you can save the lines in an array, then have a flag for the "some_pattern" and finally print if it exists. I will give it a try in a while.
-
fedorqui almost 10 years@Laurent just updated with proper solution for this requirement.
-
fedorqui over 6 years@Dining Philosopher thanks a lot for your edition! Both for the bugfix and improvement of the explanation :)
-
Brandt over 2 yearsExactly what I was looking for. Thanks!