내 폴더에는 다음과 같이 다양한 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;;;;;
간단한 셸 루프를 사용하여 두 솔루션 중 하나를 모든 파일에 적용할 수 있습니다.
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
)으로 정렬되어 빈 필드가 나타납니다.뒤쪽에Null이 아닌 필드의 경우 두 번째 정렬은 첫 번째 필드( =uniq)를 기준으로 정렬 하고 동일한 첫 번째 필드(=uniq)가 있는 초과 행을 제거하지만 그렇지 않은 경우 -k1,1
첫 번째 정렬(=stable) -u
에서 설정한 순서를 유지합니다.-s
이는 제목에서 알 수 있듯이 "가장 긴" 줄이 아닌 "가장 완전한" 줄을 실제로 원한다고 가정합니다. 첫 번째 필드가 동일한 두 줄 사이에서는 항상 짧은 쪽이하위 집합더 긴 필드에 대한 필드(IMHO는 더 짧은 행을 삭제하는 것이 적합한 유일한 경우입니다). 또한 정렬 구현에 -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