How to grep a block of text?

2019-09-06 23:41发布

I want to delete a block of specific server from this file

NameVirtualHost *
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock
</VirtualHost>
<VirtualHost *>
DocumentRoot "documentroot"
ServerName newserver
</VirtualHost>

So far I've used the following, but it doesn't seem to give me the proper result, but instead greps for each line of the block (of the inner grep) individually.

grep -vF "$(grep -w -B 2 -A 1 'newserver'/Applications/MAMP/conf/apache/httpd.conf)" /Applications/MAMP/conf/apache/httpd.conf;

The above results in the following:

NameVirtualHost *
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock

While instead I want the following:

NameVirtualHost *
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock
</VirtualHost>

标签: bash grep
4条回答
太酷不给撩
2楼-- · 2019-09-07 00:14

Another one with diff and parts of your initial command:

diff  <(grep -w -B 2 -A 1 'newserver' /Applications/MAMP/conf/apache/httpd.conf) \
      /Applications/MAMP/conf/apache/httpd.conf  | 
grep -e '^> ' | sed -e 's/^> //'
查看更多
做个烂人
3楼-- · 2019-09-07 00:18

If you want to delete an entry with ServerName newserver then use this perl one-liner:

perl -0pe 's~(?s)\s?<VirtualHost[^>]*>((?!</VirtualHost>).)*ServerName newserver.*</VirtualHost>~~' file
NameVirtualHost *
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock
</VirtualHost>

Using a variable:

s='newserver'
perl -0pe 's~(?s)\s?<VirtualHost[^>]*>((?!</VirtualHost>).)*ServerName '"$s"'.*</VirtualHost>~~' file

RegEx Demo

Explanation:

This perl command uses a regex that matches a block starting from these 2 tags:

<VirtualHost[^>]*>

and

</VirtualHost>

and finds this pattern in between these 2 patterns:

ServerName newserver

It uses a negative lookahead pattern using (?!</VirtualHost>) that makes sure only when ServerName test comes then only match is found.

查看更多
姐就是有狂的资本
4楼-- · 2019-09-07 00:30
$ tac vhost | awk 'NR>4' | tac
NameVirtualHost *
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock
</VirtualHost>

This is a 'trick' to remove the last Vhost by the known numbers of lines it contains

查看更多
Deceive 欺骗
5楼-- · 2019-09-07 00:31

awk alone:

$ awk '!/ServerName newserver/{printf "%s%s", $0, RS}' RS='</VirtualHost>' file

NameVirtualHost *
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "/Applications/MAMP/htdocs/bedrock/web"
ServerName bedrock
</VirtualHost>

RS defines </VirtualHost> as record separator and awk will prints those records($0) that doesn't match with ServerName newserver in it. And then print the RS(</VirtualHost>) again which is removed when we change RS to that.

查看更多
登录 后发表回答