awk を使用して重複部分のみを書き込む

awk を使用して重複部分のみを書き込む

awk で重複を削除するのは、かなり一般的で簡単です。しかし、1 つの列だけを比較するときに重複している行だけを印刷する必要があります。次のコマンドを試しました:

awk 'seen[$2]++'

しかし、ご覧のとおり、欠陥があります。重複した内容が出力されますが、2 回目以降にのみ出力されます。私は Unix と bash に慣れ始めたばかりなので、解決策を説明していただけるとありがたいです。

答え1

これを行うには 2 つの方法があります。

  1. ファイルを 2 回繰り返します。

    最初の反復では、各$2の出現回数をカウントします。2
    番目の反復では、カウントが1を超える行のみを出力します。

    awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
    
  2. データの 1 回の反復で:

    それぞれの2ドルが何回出現するかを数える必要があります。そして2 ドルごとにどの行が発生したかを覚えておいてください。

    この回答では、配列の配列に GNU awk を使用しています。出力の順序は、入力データと同じになる可能性は低いです。また、ファイル全体をメモリに保存する必要があります。

    gawk '
        { lines[$2][++count[$2]] = $0 }
        END {
            for (x in lines)
                if (count[x] > 1)
                    for (i=1; i<=count[x]; i++)
                        print lines[x][i]
        }
    ' file
    

入力ファイルでテスト済み:

$ cat file
a b
b b
c b
a c
a d
b d
a e

期待される出力

a b
b b
c b
a d
b d

答え2

同じサンプル入力を使用するグレン・ジャックマンの答え

$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
  • !seen[$2]++$2以前に遭遇していない 場合:
    • fl[$2]=$0この最初の行を保存します。入力はソートされておらず、重複はファイル内のどこにでも発生する可能性があると想定しているため、$2 一時的な変数ではなく、それに基づいて保存します。
    • c[$2]=1同様に、count変数を1で初期化します。
  • $2 in seen以前に発生した場合$2:
    • if(c[$2]--){print fl[$2]}まず前の行を出力し、カウンタを減算して、以降の一致で条件が失敗するようにします。
    • print現在の行を印刷する


他の入力

$ cat ip.txt 
6.2  : 897 : bar
3.1  : 32  : foo
1.2  : 123 : xyz
2.3  : 32  : baz
7.5  : 897 : boo

$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt 
3.1  : 32  : foo
2.3  : 32  : baz
6.2  : 897 : bar
7.5  : 897 : boo

順序は重複の発生状況によって異なることに注意してください

答え3

同じファイルを 2 回反復処理する場合、便利なインデックスとして行番号を使用できます。これにより、ロジックがより明確になります。

awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file

私はこの質問への回答でも同様のトリックを使いました:


このトリックの基本は、Awk が変数を参照するだけで変数を作成し、index in arrayname指定されたインデックスで配列要素が作成されたかどうかに応じて、構造が true または false を返すことです。

関連情報