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

Rah*_*til 8 sed text-processing

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

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
        }
Run Code Online (Sandbox Code Playgroud)

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

我试过用 sed 跟踪但没有用

sed '$N;$N;N;/address                 172.29.16.102/,+5d' hosts
Run Code Online (Sandbox Code Playgroud)

iru*_*var 8

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

awk -v RS= '!/172.29.16.102/{printf $0""RT}'
Run Code Online (Sandbox Code Playgroud)


slm*_*slm 5

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

然而调用的这种聪明的做法grep2倍做得非常清洁的比任何awksed解决方案,我已经看到了日期。

$ grep -v "$(grep -B 4 -A 5 'address 172.29.16.102' <file>)" <file>
Run Code Online (Sandbox Code Playgroud)

例子

这是一些示例数据。

$ 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
        }
Run Code Online (Sandbox Code Playgroud)

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

$ 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
        }
Run Code Online (Sandbox Code Playgroud)


Wil*_*ard 5

这是流编辑sed为何存在的完美示例,并且永远不会取代就地文件编辑的功能。ex

ex -c '/address  *172.29.16.103/
?{?,/}/d
x' input
Run Code Online (Sandbox Code Playgroud)

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

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

第二个命令由两个用逗号分隔的地址组成,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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我建议以交互方式编辑一些文件,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
$ 
Run Code Online (Sandbox Code Playgroud)

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