LinuxLinux [リナックス / ライナックス]オープンソースのOSカーネルで複数のテキストデータやCSVCSV [シーエスブイ]Comma-Separated Values。カンマ区切りのデータ形式ファイルを扱う際、「社員IDや商品コードをキーにして、2つのファイルをデータベースのJOINのように結合したい」「片方のファイルにしか存在しないデータ(不一致行)だけを抽出したい」といった場面はよくあります。
この記事では、2つのファイルに共通するフィールド(列)を基準として行を横方向に結合する join コマンドの使い方を解説します。基本的な内部結合の動作から、-a や -v オプションによる外部結合・不一致抽出、CSVファイルの結合、そして実行時によく遭遇する「is not in sorted order」エラーの対処法までを具体例とともに紹介します。
この記事を読むことで、join コマンドの仕様や事前ソートのルールを正しく理解し、コマンドライン上でリレーショナルなデータ結合や突合処理を簡潔に実行できるようになります。
確認した環境
本記事で解説するコマンドや出力結果は、以下の環境で実機検証を行っています(バージョン確認コマンド: join --version)。
| OSOS [オーエス]Operating System。基本ソフトウェア | GNU coreutils バージョン |
|---|---|
| DebianDebian [デビアン]コミュニティ主導で開発される安定性の高いLinuxディストリビューション GNU/Linux 11 (bullseye) | GNU coreutils 8.32(join (GNU coreutils) 8.32) |
| UbuntuUbuntu [ウブントゥ]人気のLinuxディストリビューション 24.04 LTS | GNU coreutils 9.4(join (GNU coreutils) 9.4) |
| AlmaLinux 9.4 | GNU coreutils 8.32(join (GNU coreutils) 8.32) |
Linuxのjoinコマンドとは?共通キーでファイルを結合する基本機能
join コマンドは、2つのテキストファイルから「共通するキー項目(フィールド)」を持つ行を探し出し、1行にまとめて標準出力へ書き出すコマンドです。SQLSQL [エスキューエル / シークエル]Structured Query Language。関係データベース(RDB)の管理や操作を行うデータベース言語における JOIN 句に近い感覚で、テキストデータ同士の突き合わせ(マージ)を行えます。
paste・cat・awkコマンドとの違いと使い分け
Linuxで複数のファイルを結合・加工するコマンドには paste や cat、awk などもあります。それぞれの役割と join との違いは以下の通りです。
| コマンド | 結合の仕組み・特徴 | 主な用途 |
|---|---|---|
| join | 共通するキー列の値を照合して2つのファイルを横方向に結合する(事前のソートが必要) | IDやホスト名をキーにしたマスタ結合、2ファイル間の突合・差分抽出 |
| paste | キーの照合は行わず、同じ行番号同士をそのまま横方向に結合する | 行順が完全に揃っている別ファイルの単純連結、縦並びデータの1行化 |
| cat | 複数のファイルを縦方向(行単位)にそのまま連結する | 分割されたログファイルやテキストの縦方向の結合 |
| awk | 連想配列などを用いて任意の条件でデータを照合・抽出・加工する | ソート順を変えずに結合したい場合や、複雑な条件分岐・集計を伴う処理 |
単純に1行目同士・2行目同士を横に並べたい場合はpasteコマンド、特定の列だけを切り出したい場合はawkコマンドやcutコマンドを使用します。一方、欠番や行数の違いがある2つのデータを「IDが一致する行同士」で簡潔に結び付けたい場合は join コマンドが適しています。
基本構文と主なオプション一覧
join コマンドの基本構文は以下の通りです。
join [オプション] <ファイル1> <ファイル2><ファイル1> または <ファイル2> に -(ハイフン)を指定した場合は、標準入力からデータを読み取ります。また、オプションで指定しない限り、デフォルトでは各ファイルの1列目(第1フィールド)が結合キーとして扱われ、空白文字(スペースまたはタブ)が区切り文字となります。
join コマンドでよく使用する主なオプションは以下の通りです。
| オプション | 役割 |
|---|---|
| オプションなし | 両方のファイルでキーが一致した行のみを出力する(SQLの内部結合 INNER JOIN に相当) |
-1 <列番号> | <ファイル1> の結合キーとして使用する列番号を指定する(デフォルトは 1) |
-2 <列番号> | <ファイル2> の結合キーとして使用する列番号を指定する(デフォルトは 1) |
-t <文字> | 入出力のフィールド区切り文字を <文字> に変更する(CSVなら -t ',') |
-a <ファイル番号> | キーが一致しない行も含めて出力する(1 または 2 を指定。外部結合 OUTER JOIN に相当) |
-v <ファイル番号> | キーが一致しなかった行のみを出力する(1 または 2 を指定) |
-e <文字列> | 外部結合時にデータが存在しない空フィールドを <文字列> で埋める(-o と併用) |
-o <書式> | 出力する列の順番や項目を指定する(例: 1.1,2.2 または auto) |
--header | 各ファイルの1行目をヘッダー行として扱い、ソート順のチェックを行わずに先頭へ結合出力する |
-i(--ignore-case) | キーの比較時にアルファベットの大文字・小文字の違いを無視する |
【基本】2つのファイルを内部結合・外部結合する方法
まずはサンプルファイルを作成し、キーが一致する行の結合(内部結合)や、一致しない行も含める結合(外部結合)の基本動作を確認します。
以下のように、社員IDと名前が記録された users.txt と、社員IDと部署名が記録された depts.txt を用意します。どちらのファイルも第1フィールド(社員ID)の昇順で並んでいます。
$ cat << 'EOF' > users.txt001 alice002 bob003 charlieEOF
$ cat << 'EOF' > depts.txt001 sales002 dev004 hrEOFデフォルト動作:両方のファイルに存在するキーのみ結合(内部結合)
オプションを付けずに join users.txt depts.txt を実行すると、両方のファイルの1列目に共通して存在する 001 と 002 の行だけが結合されて出力されます。
$ join users.txt depts.txt001 alice sales002 bob dev出力される列の順番は、デフォルトで「結合キー → ファイル1の残りの列 → ファイル2の残りの列」となります。片方にしか存在しない 003(users.txt のみ)と 004(depts.txt のみ)は出力されません。
-aオプション:一致しない行も残して出力する(外部結合)
SQLの左外部結合(LEFT OUTER JOIN)や完全外部結合(FULL OUTER JOIN)のように、相手側のファイルにキーが存在しない行も出力結果に残したい場合は、-a オプションにファイル番号(1 または 2)を指定します。
# ファイル1(users.txt)の不一致行(003)も残す(左外部結合に相当)$ join -a 1 users.txt depts.txt001 alice sales002 bob dev003 charlie
# 両方のファイルの不一致行(003 と 004)をすべて残す(完全外部結合に相当)$ join -a 1 -a 2 users.txt depts.txt001 alice sales002 bob dev003 charlie004 hr-eと-o auto:欠損値(空欄)を特定の文字列で埋める
上の join -a 1 -a 2 の出力を見ると、003 charlie や 004 hr のようにフィールド数が2つしかなく、どちらのファイルの列が欠けているのか判別しにくい状態になっています。
欠損しているフィールドに NONE や NULL などの代替文字列を入れて列数を揃えたい場合は、-e <文字列> オプションと -o auto オプションを組み合わせて実行します。
$ join -a 1 -a 2 -e "NONE" -o auto users.txt depts.txt001 alice sales002 bob dev003 charlie NONE004 NONE hr-o auto を指定することで、各ファイルの1行目の列数をもとに全体の出力フォーマットが自動決定され、欠損箇所(003 の部署名や 004 の名前)が NONE で埋められて3列に揃います。-e オプションは単独では機能せず、-o(auto または列指定)と併用する必要がある点に注意してください。
-vオプション:一致しなかった行(差分)だけを抽出する
結合結果ではなく、「ファイル1にあるがファイル2にはない行」のようにキーが一致しなかった行だけを取り出したい場合は、-v オプションにファイル番号(1 または 2)を指定します。
# ファイル1(users.txt)にしか存在しないキーの行を抽出する$ join -v 1 users.txt depts.txt003 charlie
# ファイル2(depts.txt)にしか存在しないキーの行を抽出する$ join -v 2 users.txt depts.txt004 hrマスタデータとトランザクションデータの突合チェックにおいて、「未登録のIDが含まれていないか」を洗い出す用途で重宝します。
【実践】結合キーの列指定・出力列の並び替え・CSVファイルの結合
実務のデータでは「結合キーが1列目ではない」「カンマ区切りのCSVでヘッダー行が付いている」といったケースが多くあります。それぞれの対応方法を確認します。
-1・-2・-oオプション:2列目以降をキーにして出力列を指定する
結合キーが1列目以外にある場合は、-1 <列番号> でファイル1のキー列、-2 <列番号> でファイル2のキー列を指定します。
ただし重要な前提として、各ファイルは「結合キーとして指定する列」の順番で事前にソートされている必要があります。以下の例では、emp.txt の2列目(部署コード)と dept_master.txt の1列目(部署コード)をキーにして結合します。
$ cat << 'EOF' > emp.txtalice 101bob 102charlie 101EOF
$ cat << 'EOF' > dept_master.txt101 Sales102 DevelopmentEOF
# emp.txt を結合キーである2列目(-k 2,2)で事前にソートする$ sort -k 2,2 emp.txt > emp_sorted.txt
# ファイル1の2列目(-1 2)とファイル2の1列目(-2 1)で結合する$ join -1 2 -2 1 emp_sorted.txt dept_master.txt101 alice Sales101 charlie Sales102 bob Developmentemp_sorted.txt には部署コード 101 を持つ行が2行(alice と charlie)存在しますが、join コマンドは1対多(または多対多)のキーに対しても自動的にそれぞれの行へマスタ側の値(Sales)を結合して出力します。
このとき、「先頭に部署コード(結合キー)が出力されるのを変えて、名前・部署名・部署コードの順に並べたい」という場合は、-o オプションで <ファイル番号>.<列番号>(結合キー自体は 0)をカンマ区切りまたは空白区切りで指定します。
# ファイル1の1列目(1.1)、ファイル2の2列目(2.2)、結合キー(0)の順に出力する$ join -1 2 -2 1 -o 1.1,2.2,0 emp_sorted.txt dept_master.txtalice Sales 101charlie Sales 101bob Development 102不要な列を除外して結合したい場合も、同様に -o オプションを使うことで後からcutコマンドを実行せずに1コマンドで整形できます。
-tと—header:ヘッダー付きのCSVファイルを結合する
カンマ(,)区切りのCSVファイルを結合する場合は、-t ',' で区切り文字を指定します。さらに1行目にカラム名(ヘッダー)が含まれている場合は、--header オプションを付けることで、1行目をソート対象外の見出しとして扱い、結果の先頭に結合して出力できます。
$ cat << 'EOF' > users.csvid,name,role001,alice,admin002,bob,editor003,charlie,viewerEOF
$ cat << 'EOF' > scores.csvid,score001,95002,82004,70EOF
# カンマ区切り(-t ',')かつ1行目をヘッダー(--header)として結合する$ join -t ',' --header users.csv scores.csvid,name,role,score001,alice,admin,95002,bob,editor,82タブ(\t)区切りのTSVファイルを結合したい場合は、BashのANSI-Cクォーテーションを使って join -t $'\t' file1.tsv file2.tsv と記述します。
「is not in sorted order」エラーの原因とsort連携の注意点
join コマンドを使用する際にもっともつまずきやすいのが、事前のソート順に関するエラーです。原因と確実な対処法を確認しておきましょう。
エラーが発生する原因(事前ソートが必須である理由)
join コマンドは、2つのファイルを先頭から順番に読み進めながらキーを比較するアルゴリズムを採用しています。そのため、両方のファイルが結合キーの文字列順(辞書順)で昇順ソートされていないと、正しく一致判定ができずエラーで停止します。
$ cat << 'EOF' > unsorted1.txt002 bob001 aliceEOF
$ cat << 'EOF' > unsorted2.txt001 sales002 devEOF
# キーが昇順に並んでいないためエラーになり、001 alice が結合されない$ join unsorted1.txt unsorted2.txtjoin: unsorted1.txt:2: is not sorted: 001 alice002 bob devjoin: input is not in sorted orderプロセス置換 <(...) と sort コマンドで一時ファイルを作らずに結合する
未ソートのファイルを結合する場合、わざわざソート済みの一時ファイルを作成しなくても、Bashのプロセス置換 <(command) とsortコマンドを組み合わせることで1行のコマンドで安全に結合できます。
# 両ファイルを1列目(-k 1,1)でソートしながらそのまま join に渡す$ join <(LC_ALL=C sort -k 1,1 unsorted1.txt) <(LC_ALL=C sort -k 1,1 unsorted2.txt)001 alice sales002 bob devここで実務上の重要なポイントが2つあります。
sortには-n(数値ソート)を付けない:joinコマンドはキーを数値ではなく**文字列(辞書順)**として比較します。sort -nで数値順に並べると(例:2の後に10が来る)、辞書順(10の後に2が来る)を期待するjoin側でソート順エラーになります。LC_ALL=Cと-k <列>,<列>を明示する: 環境のロケール設定による並び順のズレを防ぐため、LC_ALL=Cを付けてバイト値順に統一し、sort -k 1,1のように結合キーの列のみをソート基準に指定すると確実です(CSVの場合はLC_ALL=C sort -t ',' -k 1,1)。--nocheck-orderでエラーを隠さない:joinにはソート順のチェックを無効化する--nocheck-orderオプションがありますが、これを指定してもエラー表示が消えるだけで、未ソートの行(先ほどの例では001 alice)は結合されずに欠落します。必ず事前にsortを行ってください。
まとめ:joinコマンドでキーを基準にしたファイル結合を効率化しよう
本記事では、Linuxの join コマンドによる共通キーを基準としたファイル結合の方法と、実務での注意点を解説しました。
記事の要点を以下にまとめます。
joinコマンドは、2つのファイルの共通キー(デフォルトは1列目)を照合して横方向に内部結合する。-aオプションで一致しない行も含めた外部結合ができ、-vオプションを使えば片方のファイルにしか存在しない不一致行だけを抽出できる。- CSVファイルを結合する際は
-t ','を指定し、1行目が見出しの場合は--headerを併用する。 - 入力ファイルは事前に結合キーの**辞書順(文字列順)**でソートされている必要があり、
join <(LC_ALL=C sort -k 1,1 file1) <(LC_ALL=C sort -k 1,1 file2)のようにプロセス置換を使うと一時ファイルなしで安全に結合できる。
行番号順の単純結合を行う paste コマンドや、ほかのテキスト検索・加工コマンドとの組み合わせについては、Linuxのテキスト処理・検索コマンドの使い方まとめもあわせて参考にしてください。
以上で本記事の解説を終わります。
よいITライフを!
人気記事
- 1
- 2
- 3
- 4
- 5
Rocky Linux対応。実際にWebサーバーを公開するまでのプロセスを体験できる、最も確実で実践的なガイドブックです。