문제

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)

도움이 되었습니까?

해결책 2

The following might work for you:

sed -n '/Beginning of block/!b;:a;/End of block/!{$!{N;ba}};{/some_pattern/p}' filename

다른 팁

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 variable p=1 and starts storing the lines in the array a[].
  • If it finds some_pattern, it sets the flag f to 1, so that we know the pattern has been found.
  • When it finds End of block it resets p=0. If some_pattern had been found since the last Beginning 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 encounter Beginning 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

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

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}'
sed -n "
/Beginning of block/,/End of block/ {
   N
   /End of block/ { 
      s/some_pattern/&/p
      }
   }"

sed is efficient for such a treatment

with grep, you certainly should pass through intermediary file or array.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top