중복 사이에 가장 긴 줄을 유지하면서 첫 번째 값을 기반으로 중복 csv를 제거합니다.

중복 사이에 가장 긴 줄을 유지하면서 첫 번째 값을 기반으로 중복 csv를 제거합니다.

폴더(megadrive.txt, snes.txt)에 다음과 같이 다양한 csv 파일이 있습니다.

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)이므로 빈 필드가 오게 됩니다.~ 후에비어 있지 않은 필드와 두 번째 정렬은 첫 번째 필드( -k1,1)를 기준으로 정렬하고 동일한 첫 번째 필드( = uniq)가 있는 추가 줄을 제거 -u하지만 그렇지 않으면 첫 번째 정렬( = stable)에 의해 설정된 순서를 유지합니다 -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

관련 정보