ファイルから重複エントリを削除する

ファイルから重複エントリを削除する

以下に示すように、複数の同一で重複した項目を含むファイルがあります。

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

if(列)の組み合わせフィールド1そしてフィールド2一致する場合は、最初の一致時間を維持するだけです。だからそれ以来123そしてアルファベット最初の行の一致123そしてアルファベット2行目の場合は、1行目のみを保持します。より詳細に比較すると、最初の行と3番目の行も一致するため、最初の行だけが保持されます。

ただし、1行目と4行目の場合123そして123一致するがアルファベットそして定義一致するものがないため、両方の行が保持されます。

したがって、最終出力は次のようになります。

123 abc nhjk
123 def nhjk

答え1

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

しかし、もう一つは2倍速いです。

$ 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時間あまりにも多くのデータを処理するには。

1時間は10分の3秒に例えられます!

さらに、元のスクリプトはほとんどの時間をシステム時間(プロセスブランチ、IO実行など)で費やします。これはパフォーマンスの問題のもう1つの悪い兆候です。

元のスクリプトを実行します。

    $ 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
$

関連情報