替换给定搜索模式

时间:2015-05-21 14:36:50

标签: regex bash replace awk

我有一个包含我的Nagios主机的文件,我需要更改一些主机父母,而且我对如何用脚本解决这个问题毫无头绪。

该文件位于freebsd服务器上,我正在使用bash作为我喜欢的shell。

文件的结构如下所示:

define host{
use                    template-here
name                   testname
alias                  testalias
check_command          check-host-alive
max_check_attempts     5
contact_groups         group1
parents                parent1.abc.local   //2. Change this line
notification_interval  240
notification_period    none
notification_options   d,r
register               0
}

define host{
host_name              host2234.abc.local  //1. Search for this line
address                10.10.10.5
notification_period    07-21
use                    testname
}

我可以100%确定并且搜索文件的唯一模式是host_name行,它总是包含4个数字。

如果我执行grep,“grep -B10 -m1 2234.abc文件”我可以100%确定我将获得正在搜索的主机的正确父级包含在结果中,并且这是我被困的地方。 如何继续更改grep结果中的父项,然后将父项的新值保存到文件中。并将所有这些放在循环中,因为它不止一个主机我需要更改父级。

上面实际grep的输出如下所示:

grep -B10 -m1 2234.abc stack
max_check_attempts     5
contact_groups         group1
parents                parent1.abc.local
notification_interval  240
notification_period    none
notification_options   d,r
register               0
}

define host{
host_name              host2234.abc.local

同一文件中另一个主机的第二个示例,其中包含其他行,并且具有与第一个示例相同的父级。

grep -B10 -m1 2235.abc stack
contact_groups         group2
parents                parent1.abc.local
notification_interval  240
notification_period    none
notification_options   d,r
register               0
active_checks_enabled  1
}

define host{
host_name              host2235.abc.local

为了避免混淆,我不可能只通过文件替换父线上的整个字符串,因为我没有移动连接到当前父节点的所有主机。

我还有其他几个类似的用例,其中相同的逻辑适用,所以如果有人能给我一个正确的方向,让轮子旋转到说话,而不是仅仅发布一个完整的解决方案,我会非常高兴。 好吧,除非这是我在搜索和谷歌搜索时找不到的一个帖子的副本。

谢谢!

使用Ed Mortons脚本更新结果,它只打印文件内容而不更改行:

root@workdawg:script # cat tst.awk 
BEGIN { RS=""; ORS="\n\n" }
/2234\.abc/ { sub(/parents[^\n]+/,"parents\t\t\t*** Eureka! ***",prev) }
NR>1 { print prev }
{ prev = $0 }
END { print prev }
root@workdawg:script # awk -f tst.awk  stack
define host{
use                    template-here
name                   testname
alias                  testalias
check_command          check-host-alive
max_check_attempts     5
contact_groups         group1
parents                parent3.abc.local
notification_interval  240
notification_period    none
notification_options   d,r
register               0
}

define host{
host_name              host2234.abc.local
address                10.10.10.5
notification_period    07-21
use                    testname
}

2 个答案:

答案 0 :(得分:0)

肯定有很多方法可以执行此操作。此示例使用带有grep -nsed的bash脚本来完成此任务:

#!/bin/sh
file="whatever.conf"
# Get the line numbers from the text
find_grep_line_number() {
  local line_txt=$1
  local x
  i=0

  # Isolate the line number from grep -n before the :
  local REG_EX="^(.*):"
  if [[ $line_txt =~ $REG_EX ]]
  then
    local n=${#BASH_REMATCH[*]}
    for (( x=0; x<$n; x++ ));
    do
      #echo "$x: ${BASH_REMATCH[$x]}"
      i=${BASH_REMATCH[$x]}
    done
  fi
}

line_host_txt=`grep -n "^host_name" $file`
find_grep_line_number $line_host_txt
host_line=$i
line_parents_txt=`grep -n "^parents" $file`
find_grep_line_number $line_parents_txt
parents_line=$i

replacement_parents_line="parents                parent1.abc.local"
# Replace the parents line with a new line.
rslts=`sed -i "$parents_line c\$replacement_parents_line" $file`

我没有测试这个脚本但它应该可以工作(LoL).. sed非常适合做这些事情(http://www.cyberciti.biz/faq/unix-linux-sed-match-replace-the-entire-line-command/

答案 1 :(得分:0)

你没有显示你的预期输出,所以这是猜测,但这是你想要的吗?

$ cat tst.awk
BEGIN { RS=""; ORS="\n\n" }
/2234\.abc/ { sub(/parents[^\n]+/,"parents\t\t\t*** Eureka! ***",prev) }
NR>1 { print prev }
{ prev = $0 }
END { print prev }

$ awk -f tst.awk file
define host{
use                    template-here
name                   testname
alias                  testalias
check_command          check-host-alive
max_check_attempts     5
contact_groups         group1
parents                 *** Eureka! ***
notification_interval  240
notification_period    none
notification_options   d,r
register               0
}

define host{
host_name              host2234.abc.local  //1. Search for this line
address                10.10.10.5
notification_period    07-21
use                    testname
}

上面一次一个地读取每个空白行分隔的文本块(由RS=""提供),并始终在最近一次读取之前打印记录。如果当前记录包含2234.abc,则sub()会在打印之前替换上一记录中的parent行。