Linux diff コマンド:ファイルの比較

LinuxBeginner
オンラインで実践に進む

はじめに

この実験では、Linux を扱うソフトウェア開発者やシステム管理者にとって不可欠なツールである diff コマンドについて学習します。diff コマンドは、2 つのファイルの内容を比較し、その間の相違点を抽出するために使用されます。このスキルは、コードのバージョン管理、設定ファイルの変更箇所の確認、あるいはテキストベースのデータにおける不一致の特定などを行う際に非常に役立ちます。

ここでは、ソフトウェア開発のシナリオをシミュレーションしながら、diff コマンドを使って異なるバージョンのファイルを比較し、このコマンドが実際の現場でどのように活用されているかを理解していきます。

diff の基本的な使い方を理解する

まずは、2 つのシンプルなテキストファイルを比較して、diff コマンドの基本的な出力形式を確認してみましょう。

最初に、プロジェクトディレクトリに移動します。

cd /home/labex/project

次に、diff コマンドを使用して 2 つのファイルを比較します。

diff file1.txt file2.txt

以下のような出力が表示されるはずです。

1,2c1,2
< This is version 1 of the file.
< It contains some initial content.
---
> This is version 2 of the file.
> It contains updated content.
4c4
< This is the fourth line.
---
> This is a modified fourth line.

この出力の内容を詳しく見ていきましょう。

  • 数字(1,2c1,2 など)は、変更が発生した両方のファイルの行番号を示しています。
  • アルファベットの c は「変更(change)」を意味します。他に、a は「追加(add)」、d は「削除(delete)」を意味します。
  • < で始まる行は、1 番目のファイル(file1.txt)の内容です。
  • > で始まる行は、2 番目のファイル(file2.txt)の内容です。
  • --- は、1 番目のファイルと 2 番目のファイルの内容を区切る境界線です。

この出力から以下のことがわかります。

  1. 両方のファイルの 1 行目と 2 行目が異なっている。
  2. 両方のファイルの 4 行目が異なっている。
  3. 3 行目(出力に表示されていない行)は、両方のファイルで同一である。

Python スクリプトの比較

次に、より実践的なシナリオで diff コマンドを適用してみましょう。Python スクリプトを作成しており、その 2 つのバージョンを比較したい場面を想定します。

まず、両方のバージョンのスクリプトの内容を確認します。

cat script_v1.py

内容は以下の通りです。

def greet(name):
    print("Hello, " + name + "!")

def main():
    name = input("Enter your name: ")
    greet(name)

if __name__ == "__main__":
    main()

続いて、2 番目のバージョンを確認します。

cat script_v2.py

内容は以下の通りです。

def greet(name):
    print(f"Hello, {name.capitalize()}!")

def main():
    name = input("Enter your name: ")
    greet(name)
    print("Thank you for using this script!")

if __name__ == "__main__":
    main()

それでは、diff を使ってこれらのスクリプトを比較してみましょう。

diff script_v1.py script_v2.py

以下のような出力が表示されるはずです。

2c2
<     print("Hello, " + name + "!")
---
>     print(f"Hello, {name.capitalize()}!")
6a7
>     print("Thank you for using this script!")

この出力は以下のことを示しています。

  1. 2 行目が変更されました。挨拶の部分が f-string を使うようになり、名前の先頭が大文字になる(capitalize)ように修正されています。
  2. 新しい行(新しいバージョンの 7 行目)に、感謝のメッセージが追加されました。

統合形式(Unified Format)の使用

統合形式(-u オプション)を使用すると、特に大きなファイルを扱う場合や、変更の前後関係(コンテキスト)が重要な場合に、より読みやすい出力が得られます。

統合形式を使用して Python スクリプトを比較してみましょう。

diff -u script_v1.py script_v2.py

以下のような出力が表示されるはずです。

--- script_v1.py 2023-12-28 10:00:00.000000000 +0000
+++ script_v2.py 2023-12-28 10:05:00.000000000 +0000
@@ -1,8 +1,9 @@
 def greet(name):
-    print("Hello, " + name + "!")
+    print(f"Hello, {name.capitalize()}!")

 def main():
     name = input("Enter your name: ")
     greet(name)
+    print("Thank you for using this script!")

 if __name__ == "__main__":

この出力の内容を解説します。

  • 最初の 2 行は、比較対象のファイル名とタイムスタンプを示しています。
  • - で始まる行は、1 番目のファイル(script_v1.py)から削除された、あるいは変更された元の行です。
  • + で始まる行は、2 番目のファイル(script_v2.py)で追加された、あるいは変更後の行です。
  • -+ も付いていない行は、変更のない共通部分であり、変更箇所の周囲の状況(コンテキスト)を示しています。
  • @@ -1,8 +1,9 @@ という行は、1 番目のファイルの 1〜8 行目と、2 番目のファイルの 1〜9 行目の範囲を表示していることを示しています。

この形式は、変更箇所の周辺コードも確認できるため、多くの開発者に好まれています。

空白の違いを無視する

時として、空白(スペースやタブ)の違いが重要ではない場合があります。-w オプションを使用すると、diff はこれらの空白の違いを無視して比較を行います。

空白だけを変更した新しいバージョンのスクリプトを作成してみましょう。

注意:スクリプトに手動で空白を追加してください。コードをコピー&ペーストしただけでは、意図した空白が含まれない場合があります。

cat > script_v3.py << EOF
def greet(name):
    print(f"Hello, {name.capitalize()}!")

def main():
    name = input("Enter your name: ")
    greet(name)
    print("Thank you for using this script!")

if __name__ == "__main__":
    main()
EOF

では、script_v2.pyscript_v3.py を比較してみます。まずはオプションなしで、次に -w オプションを付けて実行します。

diff script_v2.py script_v3.py

空白の違いにより、いくつかの差分が表示されるかもしれません。次に以下を試してください。

diff -w script_v2.py script_v3.py

何も出力されないはずです。これは、空白を無視した場合、両者に違いがないことを意味します。

これは、フォーマットの微調整ではなく、純粋な内容の変更だけに集中したい場合に非常に便利です。

ディレクトリの比較

diff コマンドは、ディレクトリ全体を比較することもできます。いくつかのファイルを含む 2 つのディレクトリを作成して比較してみましょう。

ディレクトリとファイルを作成します。

echo "This is a file in dir1" > dir1/file.txt
echo "This is a file in dir2" > dir2/file.txt
echo "This file is unique to dir1" > dir1/unique1.txt
echo "This file is unique to dir2" > dir2/unique2.txt

ディレクトリを比較します。

diff -r dir1 dir2

以下のような出力が表示されるはずです。

Only in dir1: unique1.txt
Only in dir2: unique2.txt
diff -r dir1/file.txt dir2/file.txt
1c1
< This is a file in dir1
---
> This is a file in dir2

この出力は以下のことを示しています。

  1. dir1 には unique1.txt というファイルがあるが、dir2 には存在しない。
  2. dir2 には unique2.txt というファイルがあるが、dir1 には存在しない。
  3. file.txt は両方のディレクトリに存在するが、内容が異なっている。

-r オプションを使用すると、サブディレクトリも再帰的に比較されます。これは、複雑なディレクトリ構造の差異を確認する際に非常に強力です。

まとめ

この実験では、ソフトウェア開発の文脈で Linux の diff コマンドを活用する方法を学びました。学習した内容は以下の通りです。

  1. 2 つのテキストファイルを比較し、基本的な diff 出力を読み解く方法
  2. Python スクリプトの異なるバージョン間の差異を確認する方法
  3. 可読性の高い統合形式(Unified Format)を使用する方法
  4. 比較時に空白の違いを無視する方法
  5. ディレクトリ全体を再帰的に比較する方法

この実験では紹介しきれませんでしたが、diff には他にも便利なオプションがあります。

  • -y: 左右に並べて比較(サイド・バイ・サイド)
  • -i: 大文字と小文字の違いを無視
  • -b: 空白の数の違いを無視
  • -B: 空行のみの変更を無視
  • -q: ファイルが異なるかどうかのみを報告(詳細は表示しない)

これらのオプションを組み合わせることで、目的に合わせた詳細な比較が可能になります。