Linuxでディレクトリの内容を他のディレクトリと比較する方法:完全ガイド

Linuxシステム上で作業する際、ディレクトリの内容を他のディレクトリと比較することは、データの同期やバックアップ、変更点の確認において重要な作業です。本記事では、Linuxでディレクトリを比較するための基本的なコマンドから高度なツールまで、具体的な手順と実例を交えて詳しく解説します。これにより、ディレクトリの違いを効率的に把握し、システム管理や開発の作業効率を向上させることができます。

本記事のコマンド例はGNU diffutilsを使うLinux環境を想定しています。比較対象を書き換えずに違いを調べることが目的で、rsyncの例では必ず--dry-runを指定します。ファイルの内容、リンク先、権限などのどこまでを「同じ」と判定するかを、先に決めておきましょう。

目次

diffコマンドの基本的な使い方

Linuxでディレクトリの内容を比較する際に最も基本的なコマンドがdiffです。このセクションでは、diffコマンドを使用してディレクトリの差分を確認する方法を説明します。

diffコマンドの基本構文

diffコマンドの基本的な使い方は以下の通りです:

diff [オプション] ディレクトリ1 ディレクトリ2

このコマンドを実行することで、指定した2つのディレクトリ間の違いを表示します。

実例:簡単なディレクトリ比較

例えば、ディレクトリdir1とdir2を比較する場合、以下のコマンドを実行します:

diff -- "./dir1" "./dir2"

このコマンドは、dir1とdir2の直下にある同名ファイルを比較し、片側だけにある項目を報告します。共通のサブディレクトリの中までは比較しません。配下まで調べるには、後述の-rが必要です。

出力結果の読み方

diffコマンドの出力には、以下のような形式の行が含まれます:

Only in dir1: fileA
Only in dir2: fileB

これは、それぞれのディレクトリにのみ存在するファイルやディレクトリを示しています。

これが基本的なdiffコマンドの使い方です。次は、diffコマンドの便利なオプションを紹介します。

diffコマンドのオプション活用法

diffコマンドには多くのオプションがあり、より詳細な比較を行うことができます。このセクションでは、特に役立つオプションをいくつか紹介します。

-rオプション:再帰的なディレクトリ比較

-rオプションを使用することで、サブディレクトリも含めて再帰的に比較することができます。ディレクトリの内容が多層にわたる場合に非常に便利です。

diff -r -- "./dir1" "./dir2"

このコマンドは、両側にあるサブディレクトリへ降り、同じ相対パスのファイルを再帰的に比較します。片側にしかないディレクトリは、その存在を報告します。通常ファイルの内容比較であり、権限や所有者などを含む完全なバックアップ検証ではありません。

-qオプション:簡潔な出力

-qオプションを使用すると、ファイルが異なる場合に簡潔なメッセージのみを出力します。出力の量が多すぎる場合に便利です。

diff -q -- "./dir1" "./dir2"

このコマンドは、内容の違いを行単位で表示する代わりに「Files dir1/file and dir2/file differ」のようなメッセージを表示します。-q自体に再帰機能はないため、サブディレクトリも確認する場合は-rqを使います。

-uオプション:統一形式の出力

-uオプションを使うと、変更箇所がわかりやすい統一形式(Unified Format)で出力されます。パッチファイルの生成やコードの差分確認によく使用されます。

diff -u -- "./dir1/file1" "./dir2/file2"

このコマンドは、file1とfile2の違いを統一形式で表示します。統一形式は、変更前後の行が一目で分かるため、コードレビューなどに非常に役立ちます。

実例:複数のオプションを組み合わせた比較

複数のオプションを組み合わせることで、さらに詳細な比較が可能です。例えば、再帰的に簡潔な出力を得るには以下のようにします:

diff -rq -- "./dir1" "./dir2"

このコマンドは、共通のサブディレクトリを再帰的に調べ、異なるファイルや片側だけの項目を簡潔に報告します。ディレクトリ名を直接指定するので、通常のドットファイルも比較対象です。dir1/*のようなシェル展開に置き換える必要はありません。

シンボリックリンク・メタデータ・バイナリの扱い

GNU diffは通常、シンボリックリンクをたどって参照先を比較します。参照先がディレクトリの外にある場合もあるため、リンク自体の参照先文字列を比較したいときは--no-dereferenceを付けます。同じリンク文字列でも参照先の内容が同じとは限りません。

diff -rq --no-dereference -- "./dir1" "./dir2"

この例は通常ファイルの内容、相対パス上の有無、リンク文字列の違いを調べるためのものです。更新日時・アクセス権・所有者・ACL・拡張属性・ハードリンクの関係まで一致しているとは判定しません。デバイスファイルやFIFO、ソケットを含むツリーも、通常ファイルだけのツリーと同じようには検証できません。

バイナリファイルの違いは通常「Binary files … differ」のように報告され、画像などを意味的に比較するわけではありません。個別の通常ファイルをバイト単位で照合するならcmp -- "./dir1/file.bin" "./dir2/file.bin"も利用できます。空白や大文字小文字を無視するオプションを追加すると、「差分なし」の意味が変わる点にも注意してください。

終了ステータスは0・1・2を区別する

  • 0:指定した比較条件で差分が見つからなかった。
  • 1:差分が見つかった。比較処理の失敗とは限らない。
  • 2:パスが存在しない、読み取れないなどの問題があった。

出力が空かどうかだけでなく、標準エラーと終了ステータスも確認します。単純なdiff ... || echo "異なる"では、読取エラーまで「違い」と誤判定します。詳細はGNU diffの実行と終了ステータス、ディレクトリ比較の公式説明を参照してください。

rsyncを使ったディレクトリ比較

rsyncは本来コピー・同期用のツールです。比較専用コマンドと違い、送信元から受信先へ何を更新する予定かを表示します。本記事では両方とも既存のローカルディレクトリを指定し、必ず--dry-runで変更を抑止します。比較中はこのオプションを外さないでください。

内容とリンクの更新予定を表示する

rsync --dry-run -rlci -- "./dir1/" "./dir2/"
  • --dry-run(-n):ファイルの転送・更新・削除を実行しない試行。
  • -r:サブディレクトリを再帰的に走査する。
  • -l:シンボリックリンクをリンクとして扱う。参照先の内容をたどる-Lとは別。
  • -c:通常ファイルの転送要否を、サイズとチェックサムで判定する。
  • -i:更新予定を項目別に表示する。ドライランだけでは、期待する変更一覧が表示されるとは限らない。

先頭が>fの行は通常ファイルの受信予定、cLはリンクの作成・変更予定などを表します。+++++++++は新規項目を示します。これらはドライランでは予定であって実行済みの変更ではありません。詳細な表示形式はrsync公式マニュアルのitemize-changesを参照してください。

チェックサム比較とquick checkの違い

rsyncの通常のquick checkは、ファイルのサイズと最終更新時刻で転送要否を判定します。同じサイズ・同じ更新時刻のまま内容が変わったファイルを見逃すことがあります。-cを付けると、同じサイズのファイルでもチェックサムを調べるため、そのような違いを検出できます。ただし読み取りI/Oが増え、大きいツリーでは時間がかかります。チェックサムの一致は、衝突の可能性を排除したバイト単位の同一性証明ではありません。

内容を読まずにサイズ・更新時刻を中心とした予備確認をする場合は、次のように実行できます。-tは更新日時を保存する設定なので、日時だけの更新予定も表示対象です。上の-cの例と、出力の意味が同じではない点に注意してください。

rsync --dry-run -rlti -- "./dir1/" "./dir2/"

比較の方向・末尾のスラッシュ・対象外の属性

  • dir1/の末尾のスラッシュは、dir1というディレクトリそのものではなく、その中身をdir2に対応させる指定です。引数の順番も必ず確認します。
  • この片方向の例だけでは、dir2にしかないファイルは通常表示されません。両側の有無を一度に調べるならdiff -rq --no-dereferenceを使うか、rsyncの送信元・受信先を入れ替えたドライランも確認します。
  • -rlciは権限・所有者・更新日時・ACL・拡張属性などの一致を検証しません。-aに置き換えても、ACL・拡張属性・ハードリンクの保存は標準では含まれません。
  • rsyncの終了ステータス0は正常終了であり、diffのように「差分なし」を意味しません。更新予定が表示されても0になるため、出力とエラーを別に確認します。

本記事では削除予定を作る--deleteは使用しません。このオプションは受信先だけの項目を削除する指定で、ドライランを外すと実際の削除につながります。比較と同期は別の作業として扱ってください。オプションの仕様はrsync公式マニュアルで確認できます。

Beyond CompareとMeldを使ったGUI比較

ファイルの並びやテキスト差分を視覚的に確認するならGUIツールも便利です。ただし、比較画面にはコピー・削除・編集機能もあるため、確認だけをするときは同期や保存の操作を実行しないようにします。

Beyond Compareの導入と基本使用法

Beyond CompareはLinuxにも対応しています。Scooter Softwareの公式ダウンロードページで、使用するディストリビューションとCPUに対応したパッケージ、利用条件、導入手順を確認してください。ファイル名や対応環境は版によって変わるため、ここでは固定のインストールコマンドは使用しません。

  1. 起動後、ホーム画面で新しいFolder Compareセッションを開きます。
  2. 左側に/home/user/dir1、右側に/home/user/dir2など、比較したいディレクトリを指定します。
  3. セッションの比較条件とファイルフィルターを確認します。サイズ・日時だけの比較なのか、内容比較も行うのかを区別してください。
  4. 通常ファイルのバイト単位の違いを調べる場合は、Compare ContentsのBinary比較を使用します。Rules-basedでは設定によって空白・改行などの違いが無視されます。
  5. 差分のあるテキストファイルを開いて内容を確認します。比較だけなら、コピー・同期・削除・変更の保存はしません。

見えない項目がある場合は、ファイル名・日付などの除外条件と表示フィルターを確認します。公式ヘルプの内容比較の種類とフォルダー比較のフィルターも参考になります。

Meldでフォルダーを並べて比較する

Meldでは2つまたは3つのフォルダーを並べて比較できます。新規比較からDirectory Comparisonを選び、対象のフォルダーを指定します。ツリー上でファイルを選択し、Enterまたはダブルクリックすると、テキストファイルの詳細比較を開けます。バイナリファイルの内容をテキスト差分として読む用途には向きません。

Meldにもコピー・削除・編集機能があります。読み取り確認にとどめ、必要なら設定済みのフィルターも確認してください。画面や名称はバージョン・言語で異なるため、Meld公式のフォルダー比較ガイドを参照します。

シェルスクリプトを用いた自動比較

定期比較では「一致」「差分あり」「比較できなかった」を分けることが重要です。以下をcompare_dirs.shとして保存します。GNU diffのリンク自体を比較する例で、入力ディレクトリは変更しません。

#!/bin/bash

if [ "$#" -ne 2 ]; then
    printf 'Usage: bash %s DIR1 DIR2\n' "$0" >&2
    exit 2
fi
if [ ! -d "$1" ] || [ ! -d "$2" ]; then
    printf 'Both arguments must be existing directories.\n' >&2
    exit 2
fi

# 相対パスには ./ を付け、名前が - のディレクトリも標準入力と区別する
case "$1" in /*) left=$1 ;; *) left=./$1 ;; esac
case "$2" in /*) right=$2 ;; *) right=./$2 ;; esac

# ifの中で実行し、差分を示す終了値1も明示的に扱う
if diff -rq --no-dereference -- "$left" "$right"; then
    status=0
else
    status=$?
fi

case "$status" in
    0) printf 'No differences within the comparison scope.\n' >&2 ;;
    1) printf 'Differences found.\n' >&2 ;;
    *) printf 'Comparison failed (status=%s).\n' "$status" >&2 ;;
esac
exit "$status"

実行例は次のとおりです。bashに渡すので、練習用スクリプトの実行権限を変更する必要はありません。最後の終了ステータスを保持するため、単にdiffの後にcatだけを実行する構成にはしていません。

bash ./compare_dirs.sh "/path/to/dir1" "/path/to/dir2"

結果の保存とcronへの組み込み

ログを残す場合は、比較する両ディレクトリの外側に、専用の新しい出力先を用意します。通常出力と標準エラーを分け、コマンド直後の終了ステータスも記録してください。>は既存ファイルを上書きするため、固定名を使うと前回の結果が消えます。比較中のツリーにログを書き込むと、そのログ自体が差分になり得ます。

cronを使う場合も、まず手動で動作を確認し、管理対象の環境で実行ユーザー・コマンドの絶対パス・ログの保存先・実行時間帯を決めます。たとえば次の形式は毎日0時0分の指定ですが、実際の時刻はcronのタイムゾーン設定に依存します。これは設定例であり、このコマンドをターミナルに直接実行するものではありません。

# crontabの設定例。実在する絶対パスに置き換える
0 0 * * * /bin/bash /absolute/path/compare_dirs.sh /absolute/path/dir1 /absolute/path/dir2

大量のファイルを比べる場合は前回実行との重複にも注意します。メール通知と組み合わせるときは、終了値1の差分通知と、終了値2などの失敗通知を区別してください。メール配送の設定が別途必要であり、ログには内部パスやファイルの情報が含まれるため、送信先・送信内容を確認してから通知処理を追加します。

  • バックアップ元とコピー先に残ったファイル内容の違いを確認する。
  • 開発用ツリーと配布用ツリーの差分を定期的に記録する。
  • 重要なディレクトリの変化を監視する。ただし権限など別の検査が必要な項目は補う。

実践演習:ディレクトリ比較の練習問題

既存のデータで試すのではなく、専用の一時ディレクトリに練習用ファイルを作ります。以下はBashで実行する準備例です。元からあるファイルを上書きしないよう、mktemp -dで新しい場所を確保します。

work=$(mktemp -d) || exit 1
mkdir -p "$work/dirA/sub" "$work/dirB/sub"
printf 'same\n' > "$work/dirA/file1.txt"
printf 'same\n' > "$work/dirB/file1.txt"
printf 'left only\n' > "$work/dirA/file2.txt"
printf 'right only\n' > "$work/dirB/file3.txt"
printf 'old\n' > "$work/dirA/sub/nested.txt"
printf 'new\n' > "$work/dirB/sub/nested.txt"
printf 'Practice directory: %s\n' "$work"

演習1:再帰あり・なしを比較する

diff -- "$work/dirA" "$work/dirB"
diff -rq --no-dereference -- "$work/dirA" "$work/dirB"

最初のコマンドでは片側だけの項目と共通のsubディレクトリが報告されますが、sub/nested.txtの内容は比較されません。2つ目ではその内容の違いも報告されます。どちらもこの練習データでは差分があるため終了値1になります。

演習2:rsyncの片方向の更新予定を比べる

rsync --dry-run -rlci -- "$work/dirA/" "$work/dirB/"
rsync --dry-run -rlci -- "$work/dirB/" "$work/dirA/"

順番を入れ替えたとき、どちら側だけのファイルが更新予定として現れるかを見ます。--dry-runがあるので同期は実行されず、練習データは変更されません。rsyncが未導入ならこの演習は飛ばし、必要な場合だけ利用環境の管理手順に従って導入してください。

演習3:GUIで同じデータを確認する

Beyond CompareのFolder CompareまたはMeldのDirectory Comparisonで、表示された一時ディレクトリのdirAとdirBを開きます。片側だけの項目と、sub/nested.txtの差分を確認してください。比較結果を見てもコピーや同期は実行しません。

演習4:自動比較の3つの結果を確認する

前述のcompare_dirs.shを使い、同一ディレクトリ同士、異なるディレクトリ、存在しないパスを渡します。表示と終了ステータスを対応させて確認してください。次の例はset -eを有効にしていないBashを想定しています。

bash ./compare_dirs.sh "$work/dirA" "$work/dirA"
printf 'status=%s\n' "$?"
bash ./compare_dirs.sh "$work/dirA" "$work/dirB"
printf 'status=%s\n' "$?"
bash ./compare_dirs.sh "$work/dirA" "$work/not-created"
printf 'status=%s\n' "$?"

期待する終了ステータスは順に0・1・2です。演習後の一時ファイルは、表示したパスが今回の練習用であることを確認してから、ご自身のファイル管理方法で片付けてください。

トラブルシューティング

diffが1を返した、または結果が空だった

終了値1は差分ありを意味します。終了値2や標準エラーの出力と区別してください。結果が空でも、再帰指定の不足、除外設定、比較中の読取失敗などがないか確認します。-qは出力形式の指定であり、サブディレクトリを調べるには-rが必要です。

パスが見つからない、Permission deniedになる

パスの綴り、作業ディレクトリ、空白を含むパスの引用符を確認します。ファイルには読み取り権限、ディレクトリの走査には必要な読み取り・検索権限が必要です。原因を調べずにchmod -R 755を実行すると、非公開ファイルを他のユーザーに読めるようにするなど、意図しない権限変更につながります。所有者や管理者と必要なアクセスだけを確認してください。

リンク切れ、循環リンク、特殊ファイルがある

リンク先の比較が目的なのか、リンク文字列の比較が目的なのかを確認します。後者ならGNU diffの--no-dereferenceを使います。リンク切れや読取不能、特殊ファイルを無視して「完全一致」と判断しないでください。

rsyncで期待したファイルが表示されない

--dry-runに加え、-iを指定しているか、送信元と受信先の向き、末尾のスラッシュ、除外指定を確認します。受信先だけのファイルが出ないのは片方向の更新予定を見ているためです。サイズ・時刻が一致していても内容が違う可能性を調べるときは-cを使います。リモート比較を行う場合は、接続先の確認と両側のrsync・読み取り権限の確認も別途必要です。

GUIとコマンドの比較結果が違う

同じパスを選んでいるか、非表示・除外フィルター、空白や改行を無視するルール、サイズ・時刻だけの判定が有効になっていないかを確認します。Beyond Compareでは内容比較の種類も確認してください。設定をいきなり初期化したり再インストールしたりする前に、比較条件をそろえて小さな練習データで再確認します。

比較中にファイルが変更される、処理が遅い

更新中のディレクトリでは、走査の途中で内容が変わったりファイルが消えたりします。一度の実行結果を、ある瞬間の完全なスナップショットとは扱えません。書き込みを止められる時間帯や、別途用意した整合性のあるコピーで比べる方法を検討してください。-cやバイト単位の比較は大量の読み取りを伴うため、時間とI/O負荷も見込みます。

まとめ

Linuxでディレクトリを比べる基本は、再帰的な一覧ならdiff -rq、テキストの詳細ならdiff -u、同期予定の確認ならrsync --dry-runと-iです。GUIではBeyond CompareやMeldを利用できます。どの方法でも、リンク・属性・フィルターの扱いと終了ステータスを確認し、比較と実際の同期・削除を分けることが安全な作業につながります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次