
我的資料夾中有許多不同的 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