【问题标题】:removing hosts from a comma delimited file从逗号分隔的文件中删除主机
【发布时间】:2020-04-21 02:37:39
【问题描述】:

我正在尝试编写一种从 Nagios Core 中的主机组文件中删除主机的方法。 主机组文件格式为:

server1,server2,server3,server4

删除服务器时,我不仅需要删除服务器,还需要删除服务器后面的逗号。所以在我上面的例子中,如果我要删除 server2,文件会产生如下结果

server1,server3,server4

所以我已经用谷歌搜索并测试了以下内容,它可以删除 server2 和它后面的逗号(我不知道 b 的确切用途)

sed -i 's/\bserver2\b,//g' myfile

我想要做的是将主机名列表提供给一个小脚本,以删除一堆主机(及其后面的逗号),类似于以下内容。问题在于放置像 $x 这样的变量会破坏脚本,因此什么也不会发生。

#!/bin/ksh
for x in `cat /tmp/list`
do
sed -i 's/\b${x}\b,//g' myfile
done

我认为我非常接近这里的解决方案,但可以使用一些帮助。非常感谢您的热心帮助。

【问题讨论】:

  • 与 ${x} 的问题无关,但“for x in cat /tmp/list”有效,但首选是“while read -r x ... done/list” - 避免生成另一个进程并避免无用地使用 cat
  • 更多关于在 bash 中一次读取一行文件:mywiki.wooledge.org/BashFAQ/001

标签: bash nagios


【解决方案1】:

使用单引号告诉 shell 不要替换 ${x} - 如果你想用谷歌搜索它,它会关闭变量插值。 https://www.tldp.org/LDP/abs/html/quotingvar.html。因此,请在 sed 替换字符串周围使用双引号:

while read -r x; do sed -i "s/\b${x},\b//g" myfile; done < /tmp/list

但是由于最后一个字段后面没有逗号,运​​行两个 sed 命令可能是个好主意,一个查找 \bword,\b 另一个查找 ,word$ - 其中 \b 是一个单词边界和 $ 是行尾。

while read -r x; do sed -i "s/\b${x},\b//g" myfile; sed -i "s/,${x}$//" myfile ; done < /tmp/list

另一种可能的边界条件 - 如果您只有 server2 单独在一行上,而这就是您要删除的内容,该怎么办?也许添加第三个 sed,但这个会留下一个你可能想要删除的空行:

while read -r x
do
  sed -i "s/\b${x},\b//g" myfile  # find and delete word,
  sed -i "s/,${x}$//" myfile      # find and delete ,word
  sed -i "s/^${x}$//" myfile      # find word on a line by itself
done < t

【讨论】:

    【解决方案2】:

    这很好用:

    #!/bin/bash
    IN_FILE=$1
    shift; sed -i "s/\bserver[$@],*\b//g" $IN_FILE; sed -i "s/,$//g" $IN_FILE
    

    如果您像 ./remove_server.sh myfile "1 4" 一样为包含 server1,server2,server3,server4 的示例文件调用它,您会得到以下输出:

    server2,server3
    

    简要说明它的作用:

    • shift 将参数向下移动 1(确保 "myfile" 未输入到正则表达式中)
    • 首先sed 使用作为参数提供的数字在字符串中删除服务器(例如“1 4”)
    • 第二个sed 查找尾随逗号并将其删除
    • \b 匹配单词边界

    这是学习和测试正则表达式的绝佳资源:https://regex101.com/r/FxmjO5/1。我建议您在每次遇到正则表达式问题时检查并使用它。它在很多场合都帮助了我!

    这个脚本在更一般意义上的工作示例:

    我在这个文件上试过了:

    # This is some file containing server info:
    # Here are some servers:
    server2,server3
    
    # And here are more servers:
    server7,server9
    

    ./remove_server.sh myfile "2 9" 得到这个:

    # This is some file containing info:
    # Here are some servers:
    server3
    
    # And here are more servers:
    server7
    

    【讨论】:

      【解决方案3】:

      很确定有一个纯粹的sed 解决方案,但这里有一个脚本。

      #!/usr/bin/env bash
      
      hosts=()
      
      while read -r host; do
        hosts+=("s/\b$host,\{,1\}\b//g")
      done < /tmp/list
      
      opt=$(IFS=';' ; printf '%s' "${hosts[*]};s/,$//")
      
      sed "$opt" myfile
      
      • 它不会逐行运行sed,而只运行一个sed 调用。以防万一,假设您必须删除 20+ 模式,那么 sed 也不会运行 20+ 次。

      • 如果您认为输出正常,请添加-i

      【讨论】:

      • 不错!关于多次调用,这是一个很好的观点 - 如果您有一个包含数百个条目的删除文件和一个包含数千个条目的主机文件,那么我给出的天真的解决方案将比这个答案慢得多......
      【解决方案4】:

      通过将服务器设置为 shell 变量中的正则表达式组来使用 perl 和正则表达式:

      $ remove="(server1|server4)"
      $ perl -p -e  "s/(^|,)$remove(?=(,|$))//g;s/^,//" file
      server2,server3
      

      解释:

      • remove="(server1|server4)""server1" 甚至 "server."
      • "s/(^|,)$remove(?=(,|$))//g" 双引号允许 shell 变量,删除前导逗号,后面应该是逗号或字符串结尾
      • s/^,// file 如果第一个条目被删除,则删除前导逗号

      使用 -i 开关进行 infile 编辑。

      【讨论】:

        【解决方案5】:

        bash 脚本读取服务器以从标准输入中删除,每行一个,并使用 perl 从主机文件中删除它们(作为脚本的第一个参数传递):

        #!/usr/bin/env bash
        # Usage: removehost.sh hostgroupfile < listfile
        
        mapfile -t -u 0 servers
        IFS="|"
        export removals="${servers[*]}"
        perl -pi -e 's/,?(?:$ENV{removals})\b//g; s/^,//' "$1"
        

        它将要删除的服务器读取到一个数组中,将其连接到一个管道分隔的字符串中,然后在 perl 正则表达式中使用它在一次通过文件的过程中删除所有服务器。斜杠和其他时髦字符(只要它们不是 RE 元字符)不会弄乱 perl 的解析,因为它使用环境变量而不是直接嵌入字符串。它还使用单词边界,因此删除 server2 不会删除 server22 的那部分。

        【讨论】:

          猜你喜欢
          • 2013-12-10
          • 1970-01-01
          • 2020-10-21
          • 1970-01-01
          • 2017-09-13
          • 1970-01-01
          • 2019-02-12
          • 1970-01-01
          • 2016-11-20
          相关资源
          最近更新 更多