簡単にファイル比較ができるdiffコマンドの使い方 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

Linux/Unixには、2つのファイルの差を簡単に比較できるdiffコマンドというものがあります。

誰かがファイルを編集した場合に、どの部分が編集されたのかを確認したり、プログラムのバージョン間の差分を洗い出したりする場合に便利に使えます。



diffコマンドの基本的な使い方と実行結果の見方


diffコマンドは、2つのファイルを指定するだけで実行でき、また結果も直感的にわかるので、あまり説明もいらないのですが、実行結果の細かい見方や実行時のオプションを覚えておけばより効率的に変更点を見極めることができます。


まず、下記のような2つのPHPプログラムがあったとします。

hoge2.phpは、hoge1.phpを編集して作られたものとしています。


<?php

$num = 1;

if ($num === 1) {
    echo "hello" . "\n";
}

?>

<?php
// This is test program

$num     = 1;

if ($num === 1) {
    echo "hello world" . "\n";
}
?>

hoge2.phpでは、下記の変更点を加えています。


・ 2行目にコメントを挿入
・ 4行目にインデントを挿入
・ 7行目の標準出力を「hello world」に変更
・ 8行目と9行目にあった改行を削除


では、実際にdiffコマンドでこれらの変更点がピックアップできるかを見てみます。


$ diff hoge1.php hoge2.php
1a2
> // This is test program
3c4
< $num = 1;
---
> $num     = 1;
6c7
<     echo "hello" . "\n";
---
>     echo "hello world" . "\n";
8d8
<

上記のdiffコマンドの実行結果の見方は、下記のようになります。


1a2
> // This is test program

hoge1.phpの1行目とhoge2.phpの2行目の間に相違があり、行が追加(added)されている。


3c4
< $num = 1;
---
> $num     = 1;

hoge1.phpの3行目とhoge2.phpの4行目に相違があり、内容が変更(changed)されている。

※ hoge2.phpの7行目の違いも同様に、内容が変更されている。


8d8
<

hoge1.phpとhoge2.phpの8行目の間に相違があり、行が削除(deleted)されている。

このように、相違点の上に表示される番号と英字(a または c または d)は、


[1番目に指定したファイルの行番号] [a (追加)/ c (変更)/ d (削除)] [2番目に指定したファイルの行番号]


というフォーマットで出力されます。



diffを便利に使うためのオプション


オプションなしで単純に比較した場合でも、十分に相違点がわかって便利なのですが、いくつかのオプションを組み合わせるとより柔軟なファイル比較が行えるようになります。


- スペースの数の違いは無視したい


先ほどの例の


3c4
< $num = 1;
---
> $num     = 1;

のように、単純にスペースの数だけが違う部分は、特に問題ないから無視したいという場合があります。
そういう場合は、「-b」オプションをつけると比較対象から除外してくれます。


$ diff -b hoge1.php hoge2.php
1a2
> // This is test program
6c7
<     echo "hello" . "\n";
---
>     echo "hello world" . "\n";
8d8
<

- 改行の挿入・削除の違いは無視したい


先ほどの例の


8d8
<

のように、改行が追加・削除されている箇所は無視したいという場合は、「-B」オプションをつけて実行します。


$ diff -B hoge1.php hoge2.php
1a2
> // This is test program
3c4
< $num = 1;
---
> $num     = 1;
6c7
<     echo "hello" . "\n";
---
>     echo "hello world" . "\n";

- 大文字・小文字の違いを無視したい


(ファイルによっては、大文字と小文字の違いが大きな意味を持つことがありますが)もし、大文字と小文字の違いを無視したい場合は「-i」オプションをつけます。


$num = 1;


$NUM = 1;

の違いを無視したい場合に便利です。



diffによるディレクトリの比較


diffコマンドは、ディレクトリに対してもその違いを調べることができます。

例えば、下記のような2つのディレクトリがあったとします。


$ ls foo/
hoge1.php

$ ls bar/
hoge1.php  password.txt  tmp

barディレクトリは、fooディレクトリをコピーして作ったもので、その後プログラムやファイルの追加を行っています。
この差分を洗い出したい場合は、そのまま2つのディレクトリを指定してdiffを実行します。


$ diff bar/ foo/
diff bar/hoge1.php foo/hoge1.php
2d1
< // This is test program
4c3
< $num     = 1;
---
> $num = 1;
7c6
<     echo "hello world" . "\n";
---
>     echo "hello" . "\n";
8a8
>
bar/だけに発見: password.txt
bar/だけに発見: tmp

上記のように、プログラム間の相違点を見つけるだけでなく、一方のディレクトリにしか存在しないファイルやディレクトリも見つけてくれます。
先ほど紹介したようなファイルの間の差分を見つける際に指定したオプションを指定して実行することもできます。


ディレクトリの比較の場合、そのディレクトリ以下にあるサブディレクトリもチェック対象にしたいという場合がありますが、その際には「-r」オプションをつけて実行すれば、再帰的に比較を行ってくれます


ディレクトリ同士を比較する際には、その下にあるファイル名やディレクトリ名は、同一であるものが比較対象になります。

例えば、fooディレクトリにおいていたhoge1.phpをbarディレクトリにhoge2.phpとして保存した場合、2つは違うファイルとして扱われるため、内容のチェックまでは行ってくれません。
(サブディレクトリも同様)



diffでパッチを作る


diffコマンドでは、その変更点をまとめたパッチ(patchコマンドで適用する、ファイル間の差分情報)を作ることもできます。

オリジナルファイルを配れば済んだりもしますが、元ファイルの容量が大きかったりした場合は、その変更点だけを配布したほうが効率的だったりします。

パッチには、context形式とunified形式の大きく2つの種類があります。


$ diff -c hoge1.php hoge2.php
*** hoge1.php   2009-04-01 22:21:39.000000000 +0900
--- hoge2.php   2009-04-01 22:23:03.000000000 +0900
***************
*** 1,9 ****
  >?php

! $num = 1;

  if ($num === 1) {
!     echo "hello" . "\n";
  }
-
  ?<
--- 1,9 ----
  >?php
+ // This is test program

! $num     = 1;

  if ($num === 1) {
!     echo "hello world" . "\n";
  }
  ?>

$ diff -u hoge1.php hoge2.php
--- hoge1.php   2009-04-01 22:21:39.000000000 +0900
+++ hoge2.php   2009-04-01 22:23:03.000000000 +0900
@@ -1,9 +1,9 @@
 <?php
+// This is test program

-$num = 1;
+$num     = 1;

 if ($num === 1) {
-    echo "hello" . "\n";
+    echo "hello world" . "\n";
 }
-
 ?>

2つのフォーマットの差は出力時の見易さや量の違いぐらいです。
どちらの形式でも、patchコマンドでパッチをあてることができます。



diffコマンドで作ったパッチをあてる


では、先ほど作ったhoge1.phpのプログラムをhoge2.phpへバージョンアップするためのパッチ(hogepatch.txt)をあててみます。


$ ls
hoge1.php  hogepatch.txt
$ patch -p0 < hogepatch.txt

最初の「-p0」は、パッチをあてる対象のプログラムが置かれているディレクトリ階層をあらわしています。
「-p0」はカレントディレクトリにある、と指定しているわけです。
これで、hoge1.phpのプログラムはhoge2.phpのプログラムの内容と同一になります。


逆にあてたパッチを取り除きたい(元に戻したい)という場合は


$ patch -R < hogepatch.txt

のように、同じパッチファイルを「-R」オプションつきで実行させます。
これで、hoge1.phpは元の状態に戻ります。


先ほど、2つのパッチの形式を紹介しましたが、context形式でもunified形式でもパッチのあて方、取り除き方は一緒です。

ソースの量が多いプログラムファイルなどは、こうしたパッチファイルで配布したほうが簡単に変更点を反映させることができます。


ということで、diffコマンドの使い方(とpatchコマンドを少し)を簡単に説明してみましたが、歴史のあるコマンドですので結構奥深い事に気づかされます。

規模が大きいソース管理では、バージョン管理用のソフトウェアを導入したりしますが、規模の小さいものだったり、サクっとソースの差を調べたいといったときにかなり便利に使えると思います。