我有休文件,其中包含以下详细信息:
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特雷姆编辑ex
itor,并且永远不会取代就地文件编辑的功能。
ex -c '/address *172.29.16.103/
?{?,/}/d
x' input
该命令是一种简化形式,并不那么强大,但用于说明。
第一个命令查找指定的正则表达式并将光标移动到该行。
第二个命令由两个用逗号分隔的地址组成,d
elete 命令在其上运行。 ?{?
从当前行向后搜索左大括号,/}/
从当前行向前搜索右大括号。中间的所有内容都被删除(逐行删除,因此左大括号行的开头也被删除)。
x
保存更改并退出。当然input
是文件的名称。
对于您提供的输入,此命令完全按照预期工作。
现在,我提到这可以大大改善。我们将从正则表达式开始。这里最明显的特点是句号是通配符。给定的正则表达式也可以匹配“172329-16 103”。因此,句点必须用反斜杠转义,以便它们仅匹配字面句点。
接下来是空白。我在两个空格后跟一个 *(我可以使用\+
,但我不知道 POSIX 中是否需要该功能),但是如果文件中有制表符怎么办?最好的解决方案是使用[[:space:]]
. (这看起来会更好\+
;如果有人发现这是否是 POSIX,请发表评论。)
最后,如果正则表达式是不是在文件中找到?好吧,那么文件将只是打开进行编辑,“搜索”命令将失败,并且将打印一条错误消息,并且不会执行其余给定命令 - 您将留在编辑器ex
中这样您就可以手动进行更改。但是,如果您想在脚本中自动进行编辑,您可能希望编辑器出口如果不需要进行任何更改。答案是使用g
lobal 命令,并使用该-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:]]+})'
如果我们假设文件中没有类似的行,那么它会删除不需要的特定行。