comm / join 入門 - 2つのファイルを比較・結合する

comm / join 入門 - 2つのファイルを比較・結合する

この記事で解決できること

  • comm で 2 ファイルの 共通行・差分 を 3 列で取り出せる
  • join で共通キーをもとに 2 ファイルを 横方向に結合 できる
  • 「行が出てこない」「not sorted 警告」などの ソート起因の事故 を防げる

結論(使い分け)

  • 行単位で 共通 / 差分 を知りたい → comm
  • キー列で 2 表を横に結合 したい(SQL の JOIN 相当)→ join
  • どちらも 入力がソート済みであること が絶対条件

前提(対象環境)

  • GNU coreutils(Ubuntu / 一般的な Linux ディストリ)
  • comm / join は coreutils 同梱。追加インストール不要

comm とは? 何ができるのか

結論: comm はソート済み 2 ファイルを 1 行ずつ突き合わせ、「左だけ / 右だけ / 共通」を 3 列で出力するコマンド。

comm は 2 つの ソート済み ファイルを比較し、結果を 3 列に振り分ける。

  • 1 列目: file1 だけにある行
  • 2 列目: file2 だけにある行
  • 3 列目: 両方にある行(共通行)

例として 2 つのファイルを用意する。

$ cat a.txt
apple
banana
cherry

$ cat b.txt
banana
cherry
date
$ comm a.txt b.txt
apple
		banana
		cherry
	date

列の位置はタブのインデントで表される。apple は左だけ(1 列目)、bananacherry は両方(3 列目・タブ 2 個)、date は右だけ(2 列目・タブ 1 個)。

なぜ comm はソートが必要なのか

結論: comm は前から 1 行ずつ照合する単純なマージ方式のため、入力が昇順に並んでいないと共通行を取りこぼす。

comm は両ファイルを先頭から同時に読み進める。並びが崩れていると「同じ行」を正しく検出できず、結果が壊れる。未ソートだと次の警告が出る。

comm: file 1 is not in sorted order

事前に sort を通すのが鉄則。プロセス置換を使えば一時ファイル不要で書ける。

$ comm <(sort a.txt) <(sort b.txt)

sort の照合順は ロケール依存commsort で並び順がずれると誤動作するため、迷ったら LC_ALL=C sort で固定すると安定する。

comm の列を絞り込むには?

結論: -1 -2 -3 で対応する列を抑制する。番号は「消す列」を指す点に注意。

オプションは表示したい列ではなく 抑制する列 を指定する。

やりたいこと コマンド 残る列
共通行だけ comm -12 a b 3 列目
差分だけ(共通以外) comm -3 a b 1・2 列目
file1 だけにある行 comm -23 a b 1 列目
file2 だけにある行 comm -13 a b 2 列目
$ comm -12 a.txt b.txt
banana
cherry
$ comm -23 a.txt b.txt
apple

覚え方: 番号は「いらない列を捨てる」。共通行だけ欲しいなら 1 列目と 2 列目を捨てて -12

join とは? comm と何が違うのか

結論: join は共通キー列をもとに 2 ファイルの行を横方向に連結する。SQL の INNER JOIN に相当する。

comm が行全体の一致を見るのに対し、join指定したキー列 が一致する行同士を 1 行に結合する。デフォルトのキーは各行の 1 番目のフィールド

$ cat users.txt
1 alice
2 bob
3 carol

$ cat depts.txt
1 sales
2 engineering
4 marketing
$ join users.txt depts.txt
1 alice sales
2 bob engineering

キー 12 は両方に存在するため結合される。3 carol4 marketing は相手がいないため出力されない(内部結合)。

joinキー列でソート済み であることが前提。未ソートだと join: ... is not sorted 警告とともに結果が欠落する。comm と同じく sort を先に通すこと。

join のフィールドと区切り文字を指定するには?

結論: -t で区切り文字、-1 / -2 で各ファイルのキー列番号、-o で出力列を指定する。

CSV のように区切り文字が異なる場合や、キーが 1 列目でない場合に調整する。

$ cat users.csv
1,alice,tokyo
2,bob,osaka

$ cat depts.csv
sales,1
engineering,2

users.csv はキーが 1 列目、depts.csv はキーが 2 列目。区切りはカンマ。

$ join -t, -1 1 -2 2 users.csv depts.csv
1,alice,tokyo,sales
2,bob,osaka,engineering
  • -t,: 区切り文字をカンマに変更
  • -1 1: file1 のキーは 1 列目
  • -2 2: file2 のキーは 2 列目

-o で出力列を明示できる。-o 1.1,1.2,2.1 は「file1 の 1・2 列目と file2 の 1 列目」の意味。<file>.<field> の形式で並べる。

マッチしない行も残したい(外部結合)

結論: -a で片側の非マッチ行も出力する。-a 1 は左外部結合、-a 1 -a 2 は完全外部結合に相当する。

内部結合では相手のいない行は捨てられる。残したい場合は -a を使う。

$ join -a 1 users.txt depts.txt
1 alice sales
2 bob engineering
3 carol

3 caroldepts.txt に相手がいないが、-a 1 により欠損したまま出力される。空欄を埋めたい場合は -e-o を組み合わせる。

$ join -a 1 -e '-' -o '1.1,1.2,2.2' users.txt depts.txt
1 alice sales
2 bob engineering
3 carol -

-e '-' は欠損フィールドを - で埋め、-o で出力列を固定している。

comm と join の使い分けまとめ

結論: 行集合の差分は comm、キー結合は join。いずれもソート済み入力が前提という点を必ず押さえる。

やりたいこと コマンド
2 ファイルの共通行・差分を知る comm
共通キーで 2 表を横に結合する join
重複排除・並べ替え(前処理) sort

やってはいけないこと

  • 未ソートのまま comm / join に渡す(結果が静かに欠落する)
  • commsort でロケールを混在させる(並び順のずれで誤動作)
  • join のキー列指定(-1 / -2)を忘れて 1 列目固定のまま実行する