파일에서 중복 항목 제거

파일에서 중복 항목 제거

아래와 같이 여러 개의 동일하고 중복된 항목이 포함된 파일이 있습니다.

123 abc nhjk
123 abc cftr
123 abc xdrt
123 def nhjk
123 def cftr
123 def xdrt

if (열) 조합필드 1그리고필드 2일치하는 경우 처음 일치하는 시간만 유지하면 됩니다. 그래서 그 이후로123그리고알파벳첫 번째 라인 일치123그리고알파벳두 번째 행의 경우 첫 번째 행만 유지합니다. 더 자세히 비교해 보면 첫 번째 행과 세 번째 행도 일치하므로 첫 번째 행만 유지됩니다.

그러나 첫 번째와 네 번째 행의 경우123그리고123일치하지만알파벳그리고정의일치하는 항목이 없으므로 두 행이 모두 유지됩니다.

따라서 최종 출력은 다음과 같아야 합니다.

123 abc nhjk
123 def nhjk

답변1

한 가지 방법은 원래 파일 순서가 유지되지 않을 수도 있지만 -u플래그를 사용하는 것입니다 .sort

sort -k1,1 -k2,2 -u file

파일 순서를 유지하면서 중복 제거를 완료해야 하는 경우

awk '!a[$1, $2]++' file

답변2

RobertL과 1_CR의 훌륭한 답변

보다 유연한 셸 스크립트 접근 방식을 선호하는 경우 다음 스크립트를 사용해 볼 수 있습니다.

#!/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초가 소요됩니다. 정렬된 출력을 읽는 스크립트는 380,000개 이상의 레코드를 처리하는 데 3/10초도 채 걸리지 않습니다. 원본 스크립트가 필요합니다.약 한 시간너무 많은 데이터를 처리하려면.

1시간은 10분의 3초에 비유됩니다!

또한 원본 스크립트는 대부분의 시간을 시스템 시간(프로세스 분기, 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
$

관련 정보