how to delete the duplicate lines in file except the first matched line

时间:2018-08-06 17:57:53

标签: bash shell perl sed

In the following configuration file

/etc/fine-tune.conf

We have duplicate lines as

clean_history_in_os=true

we want to delete all the lines that include clean_history_in_os=true except the first matched line in the file

what I did until now is that

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

but the problem is that sed delete all "clean_history_in_os=true" lines

I will happy to get ideas to solve this issue ,

2 个答案:

答案 0 :(得分:3)

使用Perl

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

这将使计数器在该行上递增计数,如果> 1会跳过该行,否则打印


关于如何将模式作为外壳变量传递给Perl的问题出现了。下面我假设外壳变量$VAR包含字符串clean_history...

在所有这些中,shell变量直接用作正则表达式中的模式。如果它是问题中的文字字符串,则下面的代码按给定方式运行。但是,如果可能有特殊字符,则应将其转义;因此在正则表达式中使用时,您可能需要在模式前加上\Q。作为一般说明,应该注意不要使用外壳程序的输入来运行代码(例如在/e下)。

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

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

    其中BEGIN blockBEGIN阶段在运行时之前运行(因此以下迭代不适用)。 shift在其中从@ARGV中删除了第一个元素,在上面的调用中,它是$VAR中的值,该值首先由shell插入。然后,文件名file保留在@ARGV中,因此可以在-n下进行处理(文件已打开,并且其行遍历了)

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

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

    --(位于''下的单行程序之后)标志着该程序的参数开始;然后-qr将变量$qr引入程序,并为其分配一个如上所述的值(仅使用-qr,变量$qr便获得值1,因此是一个标志)。

    任何此类选项必须在可能的文件名之前,并将它们从@ARGV中删除,以便程序可以正常处理提交的文件。

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

    在Perl程序中对其进行访问。
    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

第一个版本不太容易出错,可能更聪明。

答案 1 :(得分:2)

您可以使用此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