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 列目)、banana と cherry は両方(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 の照合順は ロケール依存。comm と sort で並び順がずれると誤動作するため、迷ったら 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
キー 1 と 2 は両方に存在するため結合される。3 carol と 4 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 carol は depts.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 で出力列を固定している。