如何在shell脚本中匹配模式匹配之前和之后删除特定字符?

时间:2017-01-02 12:41:09

标签: linux shell scripting

我希望在特定模式匹配后删除"#"

例如:

在下面的代码中,我grep代表"SOME DESC"。我希望"SOME DESC"上方和下方评论的所有行都得到未评论。

现有代码

#define service {
#        hostgroup_name          hmaster_hosts 
#        use                     local-service
#        servicegroups           SOME_GROUP
#        service_description     SOME DESC : service Service 
#        check_command           check_nrpe!check_something
#}

删除"#"

define service {
    hostgroup_name          hmaster_hosts 
    use                     local-service
    servicegroups           SOME_GROUP
    service_description     SOME DESC : service Service 
    check_command           check_nrpe!check_something
}

我尝试使用以下代码进行更改。

sed -i 's/#define service {/define service {/g' services.cfg
sed -i 's/#        hostgroup_name          /         hostgroup_name          /' services.cfg
sed -i 's/#        use                     /         use                     /' services.cfg
sed -i 's/#       servicegroups/       servicegroups/' services.cfg
sed -i 's/#        service_description     /         service_description     /' services.cfg
sed -i 's/#        check_command           /         check_command           /' services.cfg 
sed -i 's/#}/}/g' services.cfg

#的位置在代码中是不确定的,即它可以是# hostgroup_name#hostgroup_name# hostgroup_name所以我的方法dint适用于代码的某些部分。我想知道有没有更好的方法来做到这一点,无论#

的位置如何

1 个答案:

答案 0 :(得分:2)

如果你有 GNU awk,你可以尝试以下方法:

awk -v search='SOME DESC' -v RS='(^|\n)#define service \\{[^}]*\\#\\}\n' '
  index(RT, search) { RT = gensub("(^|\n)#", "\\1", "g", RT) }
  { printf "%s%s", $0, RT }
' file

上面假设注释行直接从#开始,代码紧跟在后面,如问题中的示例输入。对于使用可变数量的空格的变体解决方案,请参见底部。

这假设感兴趣的注释行是注释define service { ... }行的,如果在块内找到搜索字符串,则应该作为整体取消注释。

  • -v search='SOME DESC'传递文字字符串以搜索为Awk变量search

  • -v RS='(^|\n)#define service \\{[^}]*\\#\\}\n'将输入记录分隔符定义为RS,作为以注释掉的define service {行开头的正则表达式以注释掉的}行结尾,后跟换行符。

    • 这意味着当前记录在$0中报告的数据包含每个感兴趣的块之前的行。

    • 但是,GNU公开了RS中的正则表达式通过(非标准)变量RT匹配的实际记录终止符(分隔符)。因此,RT的值包含每次迭代中感兴趣的块。

  • index(RT, search)返回搜索字符串在当前块内的位置的从1开始的索引,如果块不包含搜索字符串,则返回0。当用作模式(布尔条件)时,关联的操作{...})仅在块包含搜索字符串时执行。

    • RT = gensub("(^|\n)#", "\\1", "g", RT)删除评论字符。 (#)从块中所有行的最开始 请注意gensub()是GNU特定的函数,特别允许使用对捕获组的引用(\1指的是匹配的捕获组(^|\n);需要额外的\ ,因为awk字符串解析过程\ - 也是前缀转义序列。)
  • { printf "%s%s", $0, RT }打印当前记录($0),后跟 - 可能未注释 - 阻止。

适用于可变数量空白的变体

awk -v search='SOME DESC' -v RS='(^|\n)[[:blank:]]*#[[:blank:]]*define[[:blank:]]+service[[:blank:]]+\\{[^}]*\\#[[:blank:]]*\\}\n' '
  index(RT, search) { RT = gensub("(^|\n)[[:blank:]]*#", "\\1", "g", RT) }
  { printf "%s%s", $0, RT }
' file

这与上面的解决方案基本相同,只是在#匹配([[:blank:]]*)之前和之后(可能是空的)空格/制表符的运行,并且非空的可变长度之间的运行define行([[:blank:]]+)的标记。