SED:模式匹配后删除5行上方和下方的4行

SED:模式匹配后删除5行上方和下方的4行

我有休文件,其中包含以下详细信息:

define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }



define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

我想要的是,搜索"address 172.29.16.102"并删除 5 行上方和之后的 4 行。

我尝试使用 sed 但不起作用

sed '$N;$N;N;/address                 172.29.16.102/,+5d' hosts

答案1

如果每个define_host部分由一个或多个换行符分隔,这正是 GNU awk 的问题类型多行记录支持是为了解决

awk -v RS= '!/172.29.16.102/{printf $0""RT}'

答案2

当我看到这类问题时,我的直觉告诉我这是一份工作grep。然而,使用前后开关 ( & ) 时grep反转 ( ) 结果的能力不允许这样做。-v-B ..-A ..

grep然而,这种调用两次的巧妙方法比我迄今为止见过的任何解决方案awk都干净得多。sed

$ grep -v "$(grep -B 4 -A 5 'address 172.29.16.102' <file>)" <file>

例子

这是一些示例数据。

$ cat sample.txt
define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

line1b
line2b
line3b
line4b
address 172.29.16.102
line5a
line4a
line3a
line2a
line1a

define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

现在,当我们运行命令时:

$ grep -v "$(grep -B 4 -A 5 'address 172.29.16.102' sample.txt)" sample.txt
define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerA_172.29.16.102
        alias                   ServerA_172.29.16.102
        address                 172.29.16.102
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }


define host{
        use                     generic-host            ; Name of host template to use
        host_name               ServerB_172.29.16.103
        alias                   ServerB_172.29.16.103
        address                 172.29.16.103
        check_command           check-host-alive
        max_check_attempts      3
        notification_interval   120
        notification_period     24x7
        }

答案3

这是一个完美的例子,说明了sed为什么s特雷姆编辑exitor,并且永远不会取代就地文件编辑的功能。

ex -c '/address  *172.29.16.103/
?{?,/}/d
x' input

该命令是一种简化形式,并不那么强大,但用于说明。

第一个命令查找指定的正则表达式并将光标移动到该行。

第二个命令由两个用逗号分隔的地址组成,delete 命令在其上运行。 ?{?从当前行向后搜索左大括号,/}/从当前行向前搜索右大括号。中间的所有内容都被删除(逐行删除,因此左大括号行的开头也被删除)。

x保存更改并退出。当然input是文件的名称。

对于您提供的输入,此命令完全按照预期工作。


现在,我提到这可以大大改善。我们将从正则表达式开始。这里最明显的特点是句号是通配符。给定的正则表达式也可以匹配“172329-16 103”。因此,句点必须用反斜杠转义,以便它们仅匹配字面句点。

接下来是空白。我在两个空格后跟一个 *(我可以使用\+,但我不知道 POSIX 中是否需要该功能),但是如果文件中有制表符怎么办?最好的解决方案是使用[[:space:]]. (这看起来会更好\+;如果有人发现这是否是 POSIX,请发表评论。)

最后,如果正则表达式是不是在文件中找到?好吧,那么文件将只是打开进行编辑,“搜索”命令将失败,并且将打印一条错误消息,并且不会执行其余给定命令 - 您将留在编辑器ex中这样您就可以手动进行更改。但是,如果您想在脚本中自动进行编辑,您可能希望编辑器出口如果不需要进行任何更改。答案是使用global 命令,并使用该-s标志来抑制 的任何输出ex

ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d
x' input

这不是相当相当于之前的命令;如果有多个大括号块具有匹配行,则此处的全局命令将把它们全部删除。无论如何,这可能就是你想要的。

如果您只想删除第一个匹配项,而在根本没有匹配项的情况下退出而不更改文件,则可以使用该x命令作为命令参数的一部分g(在执行第一个删除命令后退出文件)并q!在底部添加一个命令,以防该g命令因缺少任何匹配行而无法执行。

ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d | x
q!' input

老实说,这些命令让这个过程看起来比实际情况复杂得多;鲁棒性是以代码的极度清晰度和可读性为代价的。这是一个权衡。

我建议以交互方式编辑一些文件,以ex获得一些感觉。这样你就可以你在做什么。以交互方式执行此修复的此类编辑会话ex如下所示:

$ ex input
"input" 23L, 843C
Entering Ex mode.  Type "visual" to go to Normal mode.
:/103
        host_name               ServerB_172.29.16.103
:?{?,/}/d                             # This deletes the current block

:$p                                   # Print and move to last line

:-5,.p                                # Print some more lines to check result
        notification_interval   120
        notification_period     24x7
        }



:?}?+,.d                              # Trim whitespace
        }
:x                                    # Save and exit
$ 

POSIX 规范ex提供进一步阅读。

答案4

那这个呢?

egrep -v '^([[:space:]]+(use|host_name|alias|check_command|max_check_attempts|notification_interval|notification_period)[[:space:]]+|^define host{|^[[:space:]]+})'

如果我们假设文件中没有类似的行,那么它会删除不需要的特定行。

相关内容