![从文件中删除重复条目](https://linux22.com/image/75837/%E4%BB%8E%E6%96%87%E4%BB%B6%E4%B8%AD%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E6%9D%A1%E7%9B%AE.png)
有一个文件包含几个相同且重复的条目,如下所示:
123 abc nhjk
123 abc cftr
123 abc xdrt
123 def nhjk
123 def cftr
123 def xdrt
如果(列)的组合字段1和字段2匹配,那么只需要保留它们第一次匹配的情况。所以自从123和ABC第一行的匹配123和ABC对于第二行,只保留第一行。进一步比较,由于第一行和第三行也匹配,因此同样只保留第一行。
然而,对于第一行和第四行123和123匹配但是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
$