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

标签 linux shell scripting

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

例如:

在下面的代码中,我将为 "SOME DESC" grep。我希望在 "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 所以我的方法适用于部分代码。我想知道无论 #

的位置如何,有没有更好的方法来做到这一点

最佳答案

如果您有 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 { ... } 行的 block ,如果在其中找到搜索字符串,则应将其作为一个整体取消注释 block 。

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

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

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

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

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

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


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

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:]]+) 的标记之间的可变长度的非空运行。

关于linux - 如何在 shell 脚本中删除模式匹配前后的特定字符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41427045/

相关文章:

bash - Bash 中的错误处理

linux - 通过终端在同一窗口中打开 firefox 新选项卡

regex - 通过正则表达式匹配消除所有 URL 替换为通用单词 URL

shell - 将分隔字段转换为具有名称和值的行

c - execvp() ls 没有这样的文件或目录

linux - 在 bash 中循环每个变量一次,而不是在第二个循环中每隔一个变量循环一次?

linux - 选择 Loop - Dynamic Output using input file to read

python - 两个 IoT 应用程序能否同时访问一个蜂窝调制解调器,这样我就不必杀死 ModemManager 并失去我的互联网连接?

python - Pycharm - 如何打开非项目文件的自动完成?

java - Homebrew如何在更新JDK后自动更新JAVA_HOME?