ファイル内の重複行を楽に探すunixコマンド

先日、大量のデータから完全に重複した行を探す処理をする機会があった。そのときはどうすればいいか手間取ってしまったが、コマンドを工夫すればすぐにできる内容だった。
以下の単純なサンプルログtest.txtを例に取る。

{"location_name": "title", "timestamp_micros": 1531209434523235}
{"location_name": "product1", "timestamp_micros": 1531209437623412}
{"location_name": "product1", "timestamp_micros": 1531209437623412}
{"location_name": "title", "timestamp_micros": 1531209456903712}

たった4行なのですぐわかるが、2行目と3行目が重複している。これをコマンドで抽出したい。 このとき、以下のようなコマンドを使うと、重複行の内容とその頻度がすぐわかる。

$ uniq -c test.txt | sort -n -k 1 -r  
   2 {"location_name": "product1", "timestamp_micros": 1531209437623412}
   1 {"location_name": "title", "timestamp_micros": 1531209456903712}
   1 {"location_name": "title", "timestamp_micros": 1531209434523235}

コマンドを分解してひとつずつ見てみる。

uniqコマンド

uniqコマンドは、ファイル内から重複する行を削除して標準出力する。

$ uniq test.txt
{"location_name": "title", "timestamp_micros": 1531209434523235}
{"location_name": "product1", "timestamp_micros": 1531209437623412}
{"location_name": "title", "timestamp_micros": 1531209456903712}

重複行が消えて3行になって出力された。

注意すべきなのが、重複行が隣り合っていないと検知できないこと。使う前に行のソートが必要になることが多い。
ただし今回はアクセスログだったのでtimestamp_micro順に並んでおり、事前のソートは必要なかった。

また、uniqコマンドの便利なオプションが-c
重複行が消えずに、重複した内容とその行数も表示してくれる。

$ uniq -c test.txt
   1 {"location_name": "title", "timestamp_micros": 1531209434523235}
   2 {"location_name": "product1", "timestamp_micros": 1531209437623412}
   1 {"location_name": "title", "timestamp_micros": 1531209456903712}

sortコマンド

行をソートできるコマンド。
-nは数値として並べ替えをするオプション。これをつけないと文字コード上の順になるため、10が2より前になったりしてしまう。
-kは空白区切りやカンマ区切りのデータのとき、並べ替えの基準となるキーを指定するもの。 -rはソート結果を降順に出力するもの。

ポイントになるのが-kの使い方。
上のuniq -cコマンドの出力を見ると、実データの前に重複した数が表示されている。しかもこの数と実データの間にはスペースが空いている。
つまりこの出力をパイプで受け渡し、-k 1とすれば、1番目のキーとして重複した数をソートに使うことができる。これがuniq -c test.txt | sort -n -k 1 -rの意味だ。

もっとも今回はソートのキーが一番最初のものなので、-nさえつけていれば-kはつけなくても良いのだが、このオプションを知っておくと便利なことが多いだろう。
ちなみに-kの数値が2〜4の場合は、データが数値ではないので並べ替えは起こらないが、5にするとtimestamp_microsの値でソートされる。5というのはデータをスペース区切りとみなしたときに5番目ということ。

調べているうちに、コマンドの本を昔読んだ記憶がよみがえってきた。やっぱり使わないと覚えられない。