![根据第一个值删除重复的 csv,保留重复项之间的最长行](https://linux22.com/image/170333/%E6%A0%B9%E6%8D%AE%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%80%BC%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%20csv%EF%BC%8C%E4%BF%9D%E7%95%99%E9%87%8D%E5%A4%8D%E9%A1%B9%E4%B9%8B%E9%97%B4%E7%9A%84%E6%9C%80%E9%95%BF%E8%A1%8C.png)
我的文件夹中有许多不同的 csv 文件(megadrive.txt、snes.txt),如下所示:
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;;;;;;;;;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
在这些 CSV 中,我有很多很多行,并且许多行都有相同的第一个字段。我想批量处理这些文件,并且在每个文件中,仅保留每个第一个字段的最长行。例如,输出应该是:
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
尤其
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
两条记录的第一个字段都是重复的,但第二个条目更长,所以我想保留第二个条目末尾删除具有相同第一个字段的所有较短的行。
我怎样才能做到这一点?
答案1
我假设您的字段是由 定义的;
。并且;
字段内不能有任何内容。如果这些假设成立,您可以执行以下操作:
$ awk -F';' '{if(!a[$1]||length($0)>length(a[$1])){a[$1]=$0}}END{for(i in a){print a[i]}}' file.txt
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
然而,这有一个缺点,即需要在内存中每个第一个字段存储一行,这对于大文件来说可能是一个问题。如果是这样,您可以尝试以下方法:
$ awk '{print length($0)";"$0}' file.txt | sort -t';' -rnk1,1 | awk -F';' '++a[$2]==1' | cut -d';' -f2-
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;;;;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;;;;;;;;;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
您可以使用简单的 shell 循环将任一解决方案应用于所有文件:
for f in *txt; do
awk -F';' '{if(!a[$1]||length($0)>length(a[$1])){a[$1]=$0}}END{for(i in a){print a[i]}}' "$f" > "$f".fixed
done
或者
for f in *txt; do
awk '{print length($0)";"$0}' file.txt | sort -t';' -rnk1,1 |
awk -F';' '++a[$2]==1' | cut -d';' -f2- > "$f".fixed
done
答案2
尝试使用sort(1)
:
sort -rt';' filename | sort -t';' -usk1,1
Aerial Assault (USA);Aerial Assault (USA);Sega Master System;;1990;Sega;Shooter;;;;;0;;;;;
After Burner (World);After Burner (World);Sega Master System;;1988;Sega;Flying;;;;;0;;;;;
Air Rescue (Europe);Air Rescue (Europe);Sega Master System;;1992;Sega;Shooter;;;;;0;;;;;
Aladdin (Europe);Aladdin (Europe);Sega Master System;;1994;Sega;Platform;;;;;0;;;;;
两种排序都将使用;
作为字段分隔符 ( -t';'
)。第一个将反向排序 ( -r
),以便空字段出现后非空字段,第二个排序将按第一个字段 ( -k1,1
) 排序,并删除具有相同第一个字段 ( = uniq) 的多余行,但否则将保持第一个排序 ( = stable)-u
设置的顺序。-s
这假设您实际上想要“最完整”的行,而不是标题所说的“最长”行,即。在具有相同第一个字段的两条线之间,较短的一条始终具有子集较长字段的字段(恕我直言,这是丢弃较短行可以有意义的唯一情况)。它还假设您的排序实现有一个-s
(稳定)选项:GNU (Linux) 和 BSD 排序都有。
如果您想对一批文件执行此操作,您应该使用find
:
find dir -type f -name '*.txt' \
-exec sh -c 'for f; do sort -rt";" "$f" |
sort -t";" -usk1,1 > "$f.new" && echo mv "$f.new" "$f"; done' sh {} +
调整查找的谓词(-name
等),并且仅在您准备好破坏现有文件时才删除echo
之前的谓词。mv