diff とは、2つのファイルやディレクトリを比べて、違う行だけを表示する Linux コマンドです。 diff 旧ファイル 新ファイル と書くだけで使え、設定ファイルの変更確認、バックアップとの照合、修正内容の受け渡し(パッチ)まで幅広く活躍します。

この記事では、Ubuntu 24.04(GNU diffutils 3.10、GNU patch 2.7.6、git 2.43.0)で実際にコマンドを動かし、出力をそのまま載せています。同じ2つのファイルを、通常形式・-u・-c・-y・-r・-q で比べて並べるので、形式ごとの記号の違いがひと目で分かります。さらに、diff で作った差分を patch で適用して patch -R で戻すまでの通し手順、適用できなかったときに出る .rej ファイルの再現、終了ステータスを使ったスクリプト、git diff・vimdiff・comm との比較もまとめました。

diffコマンドの早見表

やりたいこと コマンド ポイント
2ファイルの差分を見る diff old.conf new.conf < が左、> が右のファイルの行
読みやすい形式で見る diff -u old.conf new.conf - が削除、+ が追加。パッチの標準形式
左右に並べて見る diff -y -W 100 old.conf new.conf -W で全体の幅を指定
違う行だけ左右に並べる diff -y --suppress-common-lines a b 同じ行を省く
同じか違うかだけ知る diff -q a b 違えば Files a and b differ
ディレクトリごと比べる diff -r dir1 dir2 サブディレクトリも再帰的に比較
違うファイルの名前だけ一覧 diff -rq dir1 dir2 中身は出さない
空白の違いを無視 diff -w a b -b は空白の「量」だけ無視
大文字小文字を無視 diff -i a b
空行の増減を無視 diff -B a b
色を付ける diff --color=auto -u a b 削除が赤、追加が緑
パッチを作る diff -u old new > fix.patch ディレクトリなら diff -ruN
パッチを当てる patch app.conf < fix.patch 事前確認は --dry-run
パッチを戻す patch -R app.conf < fix.patch 適用前の内容に戻る
コマンドの出力どうしを比べる diff <(cmd1) <(cmd2) bash のプロセス置換

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「diffコマンドの使い方|-uの読み方・patch適用・オプション一覧」

比較に使う2つのファイル

以降の例は、すべて次の2ファイルで実行しています。コピペで同じ結果を再現できます。

cat > old.conf <<'EOF'
# app settings
name = shop
port = 8080
debug = true
timeout = 30
log_level = info
max_items = 100
cache = on
EOF

cat > new.conf <<'EOF'
# app settings
name = shop
port = 9090
timeout = 30
log_level = info
max_items = 100
cache = on
retry = 3
EOF

変更点は3つです。port を 8080 から 9090 に変更、debug の行を削除、末尾に retry を追加しました。

出力形式を並べて比べる

通常形式(オプションなし)

diff old.conf new.conf
3,4c3
< port = 8080
< debug = true
---
> port = 9090
8a8
> retry = 3

通常形式は「どの行を、どうすれば、右のファイルになるか」を表します。

記号 意味
< 左(1つ目)のファイルにある行
> 右(2つ目)のファイルにある行
--- 左の行と右の行の区切り
3,4c3 左の3〜4行目を、右の3行目に変更(change)
8a8 左の8行目のあとに、右の8行目を追加(add)
5d4 左の5行目を削除(delete)。右では4行目のあとに当たる

a は add、c は change、d は delete の頭文字です。記号の左の数字が1つ目のファイル、右の数字が2つ目のファイルの行番号です。

ユニファイド形式(-u)

もっともよく使われる形式です。git diff も同じ形式で出力します。

diff -u old.conf new.conf
--- old.conf    2026-10-02 09:32:30.937128223 +0900
+++ new.conf    2026-10-02 09:32:30.940592102 +0900
@@ -1,8 +1,8 @@
 # app settings
 name = shop
-port = 8080
-debug = true
+port = 9090
 timeout = 30
 log_level = info
 max_items = 100
 cache = on
+retry = 3

変更箇所の前後3行(文脈行)も一緒に表示されるので、どこが変わったのかを見つけやすくなります。

コンテキスト形式(-c)

diff -c old.conf new.conf
***************
*** 1,8 ****
  # app settings
  name = shop
! port = 8080
! debug = true
  timeout = 30
  log_level = info
  max_items = 100
  cache = on
--- 1,8 ----
  # app settings
  name = shop
! port = 9090
  timeout = 30
  log_level = info
  max_items = 100
  cache = on
+ retry = 3

(先頭2行のファイル名と日時は省略しています。)変更前のかたまりと変更後のかたまりを、上下に分けて表示します。

記号 意味
*** 1,8 **** 左ファイルの1〜8行目
--- 1,8 ---- 右ファイルの1〜8行目
! 変更された行(両方のかたまりに現れる)
+ 追加された行
- 削除された行

debug の行は「削除」ですが、port の変更と隣り合っているため、まとめて !(変更)として表示されています。

左右に並べる形式(-y)

diff -y -W 60 old.conf new.conf
# app settings          # app settings
name = shop         name = shop
port = 8080          |  port = 9090
debug = true             <
timeout = 30            timeout = 30
log_level = info        log_level = info
max_items = 100         max_items = 100
cache = on          cache = on
                 >  retry = 3
中央の記号 意味
(空白) 同じ行
| 内容が違う行
< 左にだけある行
> 右にだけある行

--suppress-common-lines を足すと、違う行だけになります。

diff -y -W 60 --suppress-common-lines old.conf new.conf
port = 8080          |  port = 9090
debug = true             <
                 >  retry = 3

-W は左右を合わせた全体の幅です。既定は130桁なので、端末の幅に合わせて指定すると折り返しを避けられます。長い行は途中で切れて表示される点に気をつけます。

違いの有無だけを知る(-q と -s)

diff -q old.conf new.conf
diff -s old.conf old.conf
Files old.conf and new.conf differ
Files old.conf and old.conf are identical

-q は違うときだけメッセージを出し、同じときは何も出しません。-s は同じときにも identical と知らせます。

形式の選び方

形式 オプション 向いている場面
通常 なし 短い差分をさっと確認
ユニファイド -u レビュー、パッチ作成、共有(標準的な選択)
コンテキスト -c 変更前後をかたまりで読みたいとき
左右並び -y 行の対応を目で追いたいとき
有無だけ -q スクリプトでの判定、大量ファイルの一覧

diff -u の読み方を図解する

-u の出力は、3つの部品でできています。

--- old.conf    2026-10-02 09:32:30 +0900    ← (1) 変更前のファイル
+++ new.conf    2026-10-02 09:32:30 +0900    ← (1) 変更後のファイル
@@ -1,8 +1,8 @@                            ← (2) ハンクヘッダー
 # app settings                            ← (3) 先頭が空白:変わっていない行
 name = shop
-port = 8080                               ← (3) 先頭が - :変更前にだけある行
-debug = true
+port = 9090                               ← (3) 先頭が + :変更後にだけある行
 timeout = 30

ハンクヘッダー @@ -1,8 +1,8 @@ の意味

「ハンク」とは、ひとかたまりの変更箇所のことです。

部分 意味
-1,8 変更前ファイルの1行目から8行分がこのハンクに含まれる
+1,8 変更後ファイルの1行目から8行分がこのハンクに含まれる
- で始まる数字 変更前ファイル側の範囲
+ で始まる数字 変更後ファイル側の範囲

数え方を確かめてみます。変更前は「空白で始まる行(6行)+ - の行(2行)=8行」、変更後は「空白で始まる行(6行)+ + の行(2行)=8行」で、ヘッダーの数字と一致します。カンマの後ろは「終わりの行番号」ではなく「行数」である点が、読み違えやすいところです。

文脈行を1行に減らす -U1 で見ると、ハンクが2つに分かれます。

diff -U1 old.conf new.conf
@@ -2,4 +2,3 @@
 name = shop
-port = 8080
-debug = true
+port = 9090
 timeout = 30
@@ -8 +7,2 @@
 cache = on
+retry = 3

1つ目のハンクは、変更前の2行目から4行分が、変更後は2行目から3行分になったことを表します(debug の行が減った分)。2つ目の -8 のように行数が省かれているときは1行の意味です。

行頭の文字 意味 色(--color 使用時)
空白 両方に共通の行 なし
- 変更前にだけある行(削除) 赤
+ 変更後にだけある行(追加) 緑
@@ ハンクの位置情報 シアン
\ No newline at end of file ファイル末尾に改行がない なし

「行の変更」は、- の行と + の行の組で表されます。port の行がその例です。

よく使うオプション一覧

比較ルールを変えるオプション

空白や大文字小文字の違いを含む2ファイルで、オプションごとの結果を確かめます。

printf 'Hello World\nfoo  bar\n\nend\n' > w1.txt   # 2行目は空白2つ、3行目は空行
printf 'hello world\nfoo bar \nend\n'   > w2.txt   # 2行目は末尾に空白
コマンド 検出された差分 終了ステータス
diff w1.txt w2.txt 1〜3行目すべて 1
diff -i w1.txt w2.txt 2行目(空白)と空行 1
diff -b w1.txt w2.txt 1行目(大文字小文字)と空行 1
diff -w w1.txt w2.txt 1行目(大文字小文字)と空行 1
diff -i -b -B w1.txt w2.txt なし 0

-b を付けた出力です。空白の違いだけだった2行目が差分から消えています。

1c1
< Hello World
---
> hello world
3d2
< 
オプション 意味 使いどころ
-i 大文字小文字の違いを無視 設定キーの表記ゆれ
-b 空白の量の違いを無視(行末の空白も無視) インデント幅の調整後
-w 空白をすべて無視(a b と ab も同じ扱い) 整形ツールをかけたあと
-B 空行の増減を無視 空行の整理後
-Z 行末の空白だけ無視 末尾空白の削除後
--strip-trailing-cr 行末の CR を無視 Windows の改行(CRLF)と比べるとき
-I 正規表現 一致する行だけの変更を無視 日付やコメント行の違いを除く

-B は、単独で使うと隣り合う変更に含まれた空行までは除けない場合があります。上の例でも diff -B だけでは差分が残り、-i -b と組み合わせて初めて差分なしになりました。

出力を変えるオプション

オプション 意味
-u / -U NUM ユニファイド形式(文脈行を NUM 行に。既定3)
-c / -C NUM コンテキスト形式
-y / -W NUM 左右並び/全体の幅
-q 違いの有無だけを報告
-s 同じ場合も報告
--color=auto 端末に出すときだけ色を付ける(always は常に)
-t タブを空白に展開して桁をそろえる
--label 名前 ヘッダーのファイル名を差し替える

--color は diffutils 3.4 以降で使えます。diff --color=always -u の出力を cat -v で見ると、色コードが付いているのが分かります。

^[[36m@@ -1,8 +1,8 @@^[[0m
 # app settings
 name = shop
^[[31m-port = 8080^[[0m
^[[31m-debug = true^[[0m
^[[32m+port = 9090^[[0m

毎回付けるのが手間なら、~/.bashrc に alias diff='diff --color=auto' を書いておくと便利です。

ディレクトリ比較のオプション

オプション 意味
-r サブディレクトリも再帰的に比較
-q 違うファイルの名前だけ表示(-rq で使う)
-N 片方にしかないファイルを「空のファイル」とみなして中身を出す
-x パターン パターンに合うファイル・ディレクトリを除外
-X ファイル 除外パターンをファイルから読む

ディレクトリごと比較する(-r・-q・-N)

次の構成で確かめます。

v1/                      v2/
├── app.conf (old)       ├── app.conf (new)
├── readme.txt (同じ)    ├── readme.txt (同じ)
└── sub/                 └── sub/
    ├── c.txt  "x"           ├── c.txt  "y"
    └── only1.txt            └── only2.txt
diff -r v1 v2
diff -r v1/app.conf v2/app.conf
3,4c3
< port = 8080
< debug = true
---
> port = 9090
8a8
> retry = 3
diff -r v1/sub/c.txt v2/sub/c.txt
1c1
< x
---
> y
Only in v1/sub: only1.txt
Only in v2/sub: only2.txt

同じ内容の readme.txt は何も表示されません。片方にしかないファイルは Only in ディレクトリ: ファイル名 と出ます。まず全体像をつかみたいときは -q を足します。

diff -rq v1 v2
Files v1/app.conf and v2/app.conf differ
Files v1/sub/c.txt and v2/sub/c.txt differ
Only in v1/sub: only1.txt
Only in v2/sub: only2.txt

除外は -x です。sub を除くと、app.conf だけが残ります。

diff -rq -x sub v1 v2
# 複数の除外は -x を重ねる
diff -rq -x node_modules -x '*.log' -x .git dirA dirB
Files v1/app.conf and v2/app.conf differ

-N を付けると、片方にしかないファイルも中身つきの差分になります。only2.txt の部分を抜き出すと次のとおりです。

diff -ruN v1/sub/only2.txt v2/sub/only2.txt
--- v1/sub/only2.txt    (日時)
+++ v2/sub/only2.txt    (日時)
@@ -0,0 +1 @@
+b

@@ -0,0 +1 @@ は「変更前は0行、変更後は1行目から1行」、つまり新しく作られたファイルを意味します。パッチにファイルの追加・削除を含めたいときは -N が欠かせません。

diff で作った差分を patch で適用する

patch は、diff の出力(パッチファイル)を読んで、ファイルに同じ変更を加えるコマンドです。作成 → 事前確認 → 適用 → 取り消しの順に通して動かします。

手順1:パッチを作る

cp old.conf app.conf
cp new.conf app.conf.new
diff -u app.conf app.conf.new > fix.patch

引数は 「変更前 → 変更後」の順です。差分があると diff の終了ステータスは1になりますが、これは「差分あり」の知らせであって、エラーではありません。

手順2:--dry-run で事前に確かめる

patch --dry-run app.conf < fix.patch
checking file app.conf

--dry-run はファイルを書き換えずに、適用できるかだけを確かめます。問題なければ checking file だけが表示されます。

手順3:適用する

patch app.conf < fix.patch
diff -s app.conf app.conf.new
patching file app.conf
Files app.conf and app.conf.new are identical

適用後の app.conf は、app.conf.new と同じ内容になりました。

手順4:patch -R で元に戻す

patch -R app.conf < fix.patch
diff -s app.conf old.conf
patching file app.conf
Files app.conf and old.conf are identical

-R(reverse)は、+ と - を入れ替えて適用するオプションです。同じパッチファイルで、適用前の状態に戻せます。

バックアップを残して適用する(-b)

patch -b app.conf < fix.patch
ls
app.conf  app.conf.new  app.conf.orig  fix.patch

-b を付けると、適用前の内容が app.conf.orig に残ります。初めて当てるパッチでは付けておくと安心です。

ディレクトリ全体のパッチと -p オプション

複数ファイルの変更は、diff -ruN で1つのパッチにまとめます。

diff -ruN v1 v2 > all.patch
cp -r v1 work
cd work
patch -p1 --dry-run < ../all.patch
patch -p1 < ../all.patch
cd ..
diff -r work v2; echo "exit=$?"
checking file app.conf
checking file sub/c.txt
checking file sub/only1.txt
checking file sub/only2.txt
patching file app.conf
patching file sub/c.txt
patching file sub/only1.txt
patching file sub/only2.txt
exit=0

適用後の work は v2 と完全に一致しました(diff -r の終了ステータスが0)。-N を付けて作ったので、only1.txt の削除と only2.txt の追加も反映されています。

-pN は、パッチに書かれたパスの先頭から N 個のディレクトリを取り除く指定です。

指定 パッチ内のパス v1/sub/c.txt の扱い 実行する場所
-p0 v1/sub/c.txt のまま v1 の親ディレクトリ
-p1 sub/c.txt v1(または作業コピー)の中
-p2 c.txt sub の中
指定なし ファイル名だけ(c.txt)を使う 対象ファイルのある場所

git が出力するパッチは a/・b/ という接頭辞が付くため、patch -p1 で当てるのが定番です。

patch の主なオプション

オプション 意味
--dry-run 書き換えずに適用できるかを確認
-pN パスの先頭 N 階層を取り除く
-R 逆向きに適用(取り消し)
-b 適用前のファイルを .orig として残す
-N 適用済みに見えるパッチは飛ばす(確認の質問をしない)
-i ファイル パッチファイルを指定(< の代わり)
-d ディレクトリ 指定ディレクトリへ移動してから適用

パッチが当たらず .rej が出るケースを再現する

パッチは「変更箇所の前後の行(文脈行)」を手がかりに位置を探します。対象ファイルがパッチ作成時と違っていると、適用できない部分が .rej(reject)ファイルに保存されます。

再現:対象の行を先に書き換えておく

# パッチが書き換える予定の行を、別の値にしてしまう
sed -i 's/port = 8080/port = 3000/' app.conf
patch app.conf < fix.patch; echo "exit=$?"
ls
patching file app.conf
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file app.conf.rej
exit=1
app.conf  app.conf.new  app.conf.orig  app.conf.rej  fix.patch

パッチは port = 8080 という行を探しますが、ファイルには port = 3000 しかないため一致しませんでした。app.conf.rej には、当てられなかったハンクがそのまま入っています。

@@ -1,8 +1,8 @@
 # app settings
 name = shop
-port = 8080
-debug = true
+port = 9090
 timeout = 30
 log_level = info
 max_items = 100
 cache = on
+retry = 3

このとき app.conf 本体は書き換わっていません(port = 3000 のまま)。複数のハンクがある場合は、当てられたハンクだけが反映され、残りが .rej に入るので、中途半端な状態になることがあります。先に --dry-run で確かめる理由がここにあります。

対処の流れ

手順 やること
1 .rej を開き、当たらなかったハンクを確認する
2 対象ファイルの該当箇所を見て、何が違うのかを確かめる(diff -u で元ファイルと比べる)
3 内容を理解したうえで、手で編集して反映する
4 反映が済んだら .rej と .orig を削除する
5 次回のために、最新のファイルからパッチを作り直す

行がずれただけなら自動で当たる

対象ファイルの先頭に2行足して、位置をずらしてみます。

cp old.conf b.conf
sed -i '1i # header added\n# another' b.conf
patch b.conf < fix.patch
patching file b.conf
Hunk #1 succeeded at 3 (offset 2 lines).

文脈行が一致していれば、行番号がずれていても patch が位置を探して適用します。offset 2 lines は「2行ずれた場所に当てた」という報告です。文脈行の一部が合わない場合も、既定で前後2行までは無視して当てる仕組み(fuzz)があり、そのときは with fuzz 1 のように表示されます(fuzz の仕様は GNU patch のマニュアルで確認)。

同じパッチを2回当てたとき

patch -N app.conf < fix.patch
patching file app.conf
Reversed (or previously applied) patch detected!  Skipping patch.
1 out of 1 hunk ignored -- saving rejects to file app.conf.rej

適用済みのファイルにもう一度当てると、patch は「逆向き、または適用済みのパッチ」と判断します。-N なしでは Assume -R? [n] と質問され、ここで y と答えると変更が取り消されます。スクリプトから呼ぶときは -N を付けると、質問で止まらずに済みます。

メッセージ 意味 次の一手
Hunk #1 FAILED at 1. 文脈行が一致しなかった .rej を見て手で反映
Hunk #1 succeeded at 3 (offset 2 lines). 位置をずらして適用できた そのままでよい
Hunk #1 succeeded at 3 with fuzz 1. 文脈の一部を無視して適用 結果を目で確認
Reversed (or previously applied) patch detected! 適用済み、または逆向き 状態を確認。必要なら -R
can't find file to patch at input line 3 パスが合っていない -p の数字と実行場所を見直す

終了ステータス(0・1・2)をスクリプトで使う

diff は、結果を終了ステータスでも返します。

終了ステータス 意味
0 差分なし(同じ内容)
1 差分あり
2 比較できなかった(ファイルがない、権限がない など)
diff old.conf nothing.conf; echo "exit=$?"
diff: nothing.conf: No such file or directory
exit=2

1 は「差分があった」という正常な結果、2 が本当のエラーです。if diff ...; then だけで判定すると、1 と 2 が同じ扱いになるので、分けたいときは case で受けます。

#!/bin/bash
# check.sh : 2つのファイルが同じかを判定する
diff -q "$1" "$2" > /dev/null 2>&1
case $? in
  0) echo "同じ内容です" ;;
  1) echo "差分があります" ;;
  *) echo "比較できませんでした(ファイルを確認してください)" ;;
esac
bash check.sh old.conf old.conf
bash check.sh old.conf new.conf
bash check.sh old.conf none.conf
同じ内容です
差分があります
比較できませんでした(ファイルを確認してください)

応用:設定が変わったときだけ再読み込みする

#!/bin/bash
# 新しい設定が現在のものと違うときだけ反映する
NEW=/tmp/app.conf.new
CUR=/etc/myapp/app.conf

diff -q "$CUR" "$NEW" > /dev/null
status=$?

if [ "$status" -eq 0 ]; then
  echo "変更なし。何もしません"
elif [ "$status" -eq 1 ]; then
  diff -u "$CUR" "$NEW"                 # 変更内容を記録に残す
  cp -p "$CUR" "$CUR.bak" && cp "$NEW" "$CUR"
  echo "設定を更新しました"
else
  echo "比較できませんでした" >&2
  exit 2
fi

(このスクリプトは構成例です。パスは環境に合わせて書き換えてください。分岐の動きは上の check.sh で確認しています。)

set -e を使うスクリプトでは、差分あり(1)でもスクリプトが止まります。diff a b || true としたり、上のように status=$? で受けたりして扱います。なお、バイナリファイルが同じかどうかだけを知りたい場合は cmp が向いています。

cmp old.conf new.conf; echo "exit=$?"
old.conf new.conf differ: char 35, line 3
exit=1

コマンドの出力どうしを比べる

bash のプロセス置換 <( ) を使うと、一時ファイルを作らずにコマンドの結果を比べられます。

diff <(printf 'b\na\nc\n' | sort) <(printf 'a\nc\nd\n')
2d1
< b
3a3
> d
# 並び順が違う2つの一覧を、ソートしてから比べる
diff <(sort list1.txt) <(sort list2.txt)

# 2つのディレクトリのファイル名一覧を比べる
diff <(ls dirA) <(ls dirB)

# 標準入力と比べる(- が標準入力)
some_command | diff - expected.txt

git diff --no-index・vimdiff・comm との比較

差分を見る道具は diff だけではありません。目的に合うものを選ぶと作業が楽になります。

ツール 得意なこと 出力・操作 向いている場面
diff 行単位の差分、パッチ作成 テキスト出力 スクリプト、どの環境にもある
git diff --no-index 色付き、単語単位の差分、統計 テキスト出力(ページャ付き) Git 管理外のファイルを見やすく比べる
vimdiff 左右に並べて編集 対話画面 差分を見ながら片方へ取り込む
comm ソート済みリストの共通・固有行 3列のテキスト 「片方にだけある項目」を抜き出す
cmp バイト単位の一致確認 最初の違いの位置 バイナリの一致確認
sdiff 左右並び+対話的なマージ テキスト/対話 diff -y を対話で使いたいとき
diff3 3つのファイルの比較 テキスト出力 共通の元ファイルがある2つの変更

git diff --no-index

Git リポジトリの外でも、--no-index を付ければ任意の2ファイルを比べられます。

git diff --no-index old.conf new.conf
diff --git a/old.conf b/new.conf
index 6a3042c..7f21475 100644
--- a/old.conf
+++ b/new.conf
@@ -1,8 +1,8 @@
 # app settings
 name = shop
-port = 8080
-debug = true
+port = 9090
 timeout = 30
 log_level = info
 max_items = 100
 cache = on
+retry = 3

終了ステータスは diff と同じく、差分があれば1でした。--stat で変更量、--word-diff で単語単位の違いを出せます。

git diff --no-index --stat old.conf new.conf
git diff --no-index --word-diff old.conf new.conf
 old.conf => new.conf | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
# app settings
name = shop
port = [-8080-]
[-debug = true-]{+9090+}
timeout = 30

[-...-] が削除された語、{+...+} が追加された語です。1行が長い文章や設定で、行の中のどこが変わったかを知りたいときに役立ちます。

vimdiff

vimdiff old.conf new.conf

左右に並んだ画面で、違う行が色付きで表示されます。主なキーは次のとおりです(Vim 9.1 のヘルプで確認。対話画面のため、この記事では画面の取得はしていません)。

キー 動き
]c / [c 次の差分へ / 前の差分へ
do 相手側の内容をこちらへ取り込む(diff obtain)
dp こちらの内容を相手側へ送る(diff put)
Ctrl+w のあと w 左右のウィンドウを移動
:diffupdate 差分表示を更新
:qa 両方閉じて終了

comm

comm は、ソート済みの2ファイルを比べ、「左だけ」「右だけ」「両方」の3列に分けます。

printf 'apple\nbanana\ncherry\n' > l1.txt
printf 'banana\ncherry\ndate\n'  > l2.txt
comm l1.txt l2.txt
apple
        banana
        cherry
    date

1列目(字下げなし)が l1.txt だけ、2列目(タブ1つ)が l2.txt だけ、3列目(タブ2つ)が両方にある行です。数字のオプションは「その列を表示しない」という指定です。

コマンド 取り出せるもの 結果
comm -12 l1.txt l2.txt 両方にある行 banana、cherry
comm -23 l1.txt l2.txt l1.txt にだけある行 apple
comm -13 l1.txt l2.txt l2.txt にだけある行 date

会員IDの一覧どうしを突き合わせて「片方にだけあるID」を抜き出す、といった用途では、diff より comm のほうが結果をそのまま使えます。ソートされていない場合は comm -23 <(sort a) <(sort b) のように書きます。

つまずきやすい点と直し方

起きること 理由 直し方
全行が差分として出る 改行コードが違う(LF と CRLF) diff --strip-trailing-cr a b
見た目は同じなのに差分が出る 行末の空白、タブと空白の違い -Z や -b で確認。cat -A で見えない文字を表示
\ No newline at end of file と出る 片方の最終行に改行がない 末尾に改行を足すと差分が消える
Binary files a and b differ バイナリと判定された 一致確認だけなら cmp。テキスト扱いは -a
パッチの向きが逆になった 引数の順番が「新 → 旧」だった diff -u 旧 新 の順で作り直す。当てる側は -R でも対応可
can't find file to patch -p の数字か実行場所が合わない パッチ先頭の --- 行のパスを見て -pN を決める
set -e のスクリプトが diff で止まる 差分ありの終了ステータスが1 diff a b || true、または $? を受けて分岐
-y で行が途中で切れる 幅が足りない -W 200 のように広げる

よくある質問

Q. diff の < と > はどちらが変更前のファイルですか? A. < が1つ目(左)の引数、> が2つ目(右)の引数のファイルにある行です。diff 旧 新 の順で書けば、< が変更前、> が変更後になります。-u 形式では同じ関係が - と + で表されます。

Q. @@ -1,8 +1,8 @@ は何を表していますか? A. 変更前のファイルでは1行目から8行分、変更後のファイルでも1行目から8行分が、このかたまり(ハンク)に含まれるという意味です。カンマの後ろは行数で、省かれているときは1行です。

Q. patch を当てたあと、元に戻すにはどうすればよいですか? A. 同じパッチファイルを使って patch -R 対象ファイル < パッチ を実行します。この記事の検証でも、-R のあとに元ファイルと diff -s で比べて一致を確認しました。-b を付けて適用していれば、.orig ファイルからも戻せます。

Q. diff の終了ステータスが1なのはエラーですか? A. いいえ、1は「差分があった」という正常な結果です。エラーは2で、ファイルが見つからないときなどに返ります。スクリプトでは0・1・2を分けて扱うと、意図どおりに分岐できます。

まとめ

  • diff 旧 新 の順で書く。通常形式は < が左・> が右、a・c・d は追加・変更・削除
  • -u は - が削除、+ が追加。@@ -開始,行数 +開始,行数 @@ で位置を読む
  • 左右に並べるなら -y -W 幅、有無だけなら -q、ディレクトリは -r(一覧は -rq)
  • 空白は -b・-w、大文字小文字は -i、空行は -B、色付けは --color=auto
  • パッチは diff -u 旧 新 > fix.patch → patch --dry-run → patch → 戻すときは patch -R
  • .rej は当てられなかったハンクの保存先。中身を読んで手で反映し、パッチを作り直す
  • 終了ステータスは 0=同じ、1=差分あり、2=エラー。case で分けて扱う

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「diffコマンドの使い方|-uの読み方・patch適用・オプション一覧」

株式会社RJCでは、diff や patch を使った差分の読み方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?