代码之家  ›  专栏  ›  技术社区  ›  King David

如何删除文件中除第一个匹配行以外的重复行

  •  2
  • King David  · 技术社区  · 7 年前

    在下面的配置文件中

    /etc/fine-tune.conf
    

    我们有重复的行

    clean_history_in_os=true
    

    我们要删除所有包含 clean_history_in_os=真 除了文件中的第一个匹配行

    我到现在为止所做的就是

      sed  -i '/clean_history_in_os=true/d' /etc/fine-tune.conf
    

    但问题是sed删除了所有“clean_history_in_os=true”行

    我很乐意找到解决这个问题的办法,

    2 回复  |  直到 7 年前
        1
  •  3
  •   zdim    5 年前

    使用Perl

    perl -i -ne'next if /clean_history_in_os=true/ && ++$ok > 1; print' file
    

    在该行上,如果 > 1 它跳过线,否则会打印


    问题是,如果将模式作为shell变量,如何将其传递给Perl。下面我假设shell变量 $VAR 包含字符串 clean_history...

    在所有这些中,shell变量直接用作regex中的模式。如果是问题中的文字字符串,那么下面的代码就如给定的那样。但是,如果可能有特殊字符,则应该对其进行转义;因此,您可能希望在模式前面加上 \Q 在regex中使用时。一般来说,应该注意不要使用shell的输入来运行代码(比如 /e ).

    • 将其作为参数传递,然后在 @ARGV

      perl -i -ne'
          BEGIN { $qr=shift; }; 
          next if /$qr/ && ++$ok > 1; print
      ' "$VAR" file
      

      其中 BEGIN block 开始 阶段,在运行时之前(因此不适用于以下迭代)。在里面 shift 从中删除第一个元素 @ARGV ,在上面的调用中是 $风险价值 ,首先由shell插值。然后是文件名 file 留在 @阿格夫 ,以便在 -n (打开文件并迭代其行)

    • 使用 -s switch ,它启用程序的命令行开关

      perl -i -s -ne'next if /$qr/ && ++$ok > 1; print' -- -qr="$VAR" file
      

      这个 -- (在下面的单行程序之后 '' )标记程序参数的开始;然后 -qr 引入变量 $qr 在程序中,使用如上所述分配给它的值(仅使用 -二维码 变量 美元 获取值 1 ,国旗也是)。

      任何这样的选项必须在可能的文件名之前,并且从 @阿格夫 这样程序就可以正常地处理提交的文件。

    • 导出bash变量,使其成为一个环境变量,然后可以通过 %ENV hash

      export $VAR="clean_history..."
      perl -i -ne'next if /$ENV{VAR}/ && ++$ok > 1; print' file
      

      但我宁愿推荐前两种选择,而不是这一种。


    对注释中给出的问题的改进指定 clean_... # 那就干脆跳过这一行。这是最简单的单独测试

    next if /#$qr/; next if /$qr/ && ++$ok > 1; print
    

    或者,依靠短路

    next if /#$qr/ || (/$qr/ && ++$ok > 1); print
    

    第一个版本不太容易出错,而且可能更清晰。

        2
  •  2
  •   anubhava    7 年前

    你可以用这个 awk 要删除除第一行以外的所有匹配行:

    awk '!(/clean_history_in_os=true/ && n++)' file
    

    要将文件保存在适当的位置,可以使用 gnu awk 命令:

    awk -i inplace '!(/clean_history_in_os=true/ && n++)' file
    

    否则,将临时文件用作:

    awk '!(/clean_history_in_os=true/ && n++)' file > $$.tmp && mv $$.tmp file
    

    这是一个 sed 同样的解决方案:

    sed -i -n '0,/clean_history_in_os=true/p;/clean_history_in_os=true/!p' file