从文件中删除重复条目

从文件中删除重复条目

有一个文件包含几个相同且重复的条目,如下所示:

123 abc nhjk
123 abc cftr
123 abc xdrt
123 def nhjk
123 def cftr
123 def xdrt

如果(列)的组合字段1字段2匹配,那么只需要保留它们第一次匹配的情况。所以自从123ABC第一行的匹配123ABC对于第二行,只保留第一行。进一步比较,由于第一行和第三行也匹配,因此同样只保留第一行。

然而,对于第一行和第四行123123匹配但是ABC定义不匹配,因此这两行都被保留。

所以最终的输出应该是这样的:

123 abc nhjk
123 def nhjk

答案1

一种方法是使用-u标志 tosort尽管这最终可能不会保留原始文件顺序:

sort -k1,1 -k2,2 -u file

如果您需要在保留文件顺序的情况下完成重复数据删除

awk '!a[$1, $2]++' file

答案2

RobertL 和 1_CR 的精彩回答

如果您更喜欢更灵活的 shell 脚本方法,可以尝试以下脚本:

#!/bin/sh

rm output.txt
touch output.txt
while read line
do
    field1=$( echo $line | cut -d" " -f1)
    field2=$( echo $line | cut -d" " -f2)
    lookup="$field1 $field2"
    if  [ -z $(grep "$lookup" output.txt) ]
    then
        echo $line >> output.txt
    fi
done < input.txt
cat output.txt
exit 0

显然,它可以缩短很多,但我想把每一步都做得非常清楚。

享受。

编辑:

按照@RobertL 发布的链接并测试了几个选项后,我不得不同意这个脚本有巨大的改进。我会用

#!/bin/sh

sort -k1,2 -u "$@" |
while read line
do
     echo "$line"
done

我对此唯一的问题是向 RobertL 提出的,但为什么要使用:

sort -k1,2 -k2,2 -u

代替

sort -k1,2 -u

根据我自己的测试,你的排序是有效的,

$ cat robertL.sh
    #!/bin/sh

    sort -k1,1 -k2,2 -u "$@" |
    while read line
    do
         echo "$line"
    done

$ time ./robertL.sh < input.txt

123 abc nhjk
123 def nhjk

real    0m0.022s
user    0m0.014s
sys     0m0.009s

但另一个速度是后者的两倍

$ cat process_v2.sh
#!/bin/sh

sort -k1,2 -u "$@" |
while read line
do
     echo "$line"
done

$ time ./process_v2.sh < input.txt

123 abc nhjk
123 def nhjk

real    0m0.012s
user    0m0.006s
sys     0m0.009s

因此,作为结论,强烈推荐 RobertL 的方法,但始终将此处的所有内容作为示例,而不是作为绝对真理或问题的最终解决方案。我认为最好的办法是通过答案找到指导。

答案3

如果您需要密集处理输出的每条记录,您可以创建一个读取输出的每一行的过滤器。 不要在排序/唯一算法内处理记录。

原始脚本每处理 100 条记录大约需要 1 秒。读取排序输出的脚本只用了不到 3/10 秒的时间就处理了超过 380,000 条记录。它会采用原始​​脚本一个多小时来处理这么多的数据。

一小时与一秒的 3/10 相比!

另请注意,原始脚本也花费了大部分时间在系统时间(分叉进程、执行 io 等),这是性能问题的另一个不好的迹象。

执行原脚本:

    $ wc -l input.txt 
    1536 input.txt
    $ time ./jesus.sh
    rm: cannot remove ‘output.txt’: No such file or directory
    123 abc nhjk
    123 def nhjk

    real    0m16.997s              #<<<---------
    user    0m3.546s
    sys 0m16.329s                  #<<<---------

执行这个新的示例脚本时,只有一小部分运行时间花费在操作系统代码上:

    $ time ./RobertL.sh < input.txt
    123 abc nhjk
    123 def nhjk        

    real    0m0.011s               #<<<---------
    user    0m0.004s
    sys 0m0.007s                   #<<<---------

现在,我们在一个巨大的数据集上运行新脚本,我们知道原始脚本需要 1 个多小时才能完成:

    $ wc -l data388440.txt 
    388440 data388440.txt
    $ time ./RobertL.sh < data388440.txt 
    123 abc nhjk
    123 def nhjk        

    real    0m0.282s               #<<<---------
    user    0m0.728s
    sys 0m0.032s                   #<<<---------

新的示例脚本:

    $ cat RobertL.sh
    #!/bin/sh

    sort -k1,1 -k2,2 -u "$@" |
    while read line
    do
         echo "$line"
    done

原始脚本,修改为在不安装 ksh 的情况下运行:

    $ cat jesus.sh
    #!/bin/bash
    #!/bin/sh  # does not accept [[ ... ]]
    #!/bin/ksh # not installed on ubuntu by default

    rm output.txt
    touch output.txt
    while read line
    do
        field1=$( echo $line | cut -d" " -f1)
        field2=$( echo $line | cut -d" " -f2)
        lookup="$field1 $field2"
        if  [[ -z $(grep "$lookup" output.txt) ]]
        then
            echo $line >> output.txt
        fi
    done < input.txt
    cat output.txt
    exit 0

输入数据是通过重复原始6行样本数据创建的,数据几乎包含所有重复记录。

答案4

如果要删除的行都是连续的,并且键的长度相同,那么您可以使用:

$ uniq --check-chars=8 <<EOF
123 abc nhjk
123 abc cftr
123 abc xdrt        
123 def nhjk        
123 def cftr        
123 def xdrt
EOF         
123 abc nhjk
123 def nhjk
$

相关内容