diffコマンドは、ファイルの差分を確認するコマンドになります。異なるバージョンのファイルを比較するのに利用できます。ディレクトリ同士の比較を行う場合は、同じファイル名同士を比較して、比較結果を表示することができます。また、差分の出力をファイルに保存し、patchコマンドでファイルに対して差分を書き込むことで差分を出したファイルに変更できます。
目次
diffコマンドの利用例
2つのファイルの差分を表示
(オプションなし)
diffは、2つのファイルを引数にすることで、指定した2つのファイルの差分を表示することが出来ます。例えば、複数のバージョンがあるファイルについて、2つのファイルがどのように異なっているかを確認出来ます。
コマンド例
1 |
diff point-v1.txt point-v2.txt |
point-v1.txt
number,name,point 1,aaa,100 2,ccc,80 3,eee,90
point-v2.txt
no.,name,point 1,aaa,100 2,bbb,28 3,ccc,80 4,ddd,65 5,eee,90
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 |
1c1 < number,name,point --- > no.,name,point 3,4c3,6 < 2,ccc,80 < 3,eee,90 --- > 2,bbb,28 > 3,ccc,80 > 4,ddd,65 > 5,eee,90 |
diffの実行結果の出力形式はnormal形式の出力です。差分の表示の見方は、たとえば、
1 2 3 4 |
1c1 < number,name,point --- > no.,name,point |
で一つのまとまりになります。
normal形式の最初の一行目は、引数の左のファイルの行番号、変更内容を示すシンボル(a(add):追加・d(delete):削除・c(change):変更)、引数の右のファイルの行番号を表します。また、行番号がカンマで区切られているとその行の範囲を表しています。
その次の行からは'<'で始まる行は左のファイルにある行で、'>'で始まる行は右のファイルにある行になります。シンボルがcの場合は、'---'で変更のある行が区切られます。
他の実行結果として、行の追加や行の削除の結果は以下のようになります。
point-v1-add.txt
number,name,point 1,aaa,100 2,ccc,80 3,eee,90 4,fff,34
point-v1-delete.txt
number,name,point 1,aaa,100 3,eee,90
コマンド例と実行結果
1 2 3 4 5 6 |
$diff point-v1.txt point-v1-add.txt 4a5 > 4,fff,34 $diff point-v1.txt point-v1-delete.txt 3d2 < 2,ccc,80 |
2つのファイルを並べて表示
(-yオプション)
-yオプション(--side-by-sideオプション)はファイルの比較結果を並べて表示できます。diffコマンドで2つのファイルの内容を表示して確認したい場合に利用できます。
コマンド例
1 |
diff -y point-v1.txt point-v2.txt |
実行結果
出力行の幅を調整したい場合は、-Wオプションが利用できます。
コマンド例
1 |
diff -y -W50 point-v1.txt point-v2.txt |
実行結果
また、共通行を表示したくない場合は、--suppress-common-linesオプションが利用できます。
コマンド例
1 |
diff -y -W50 --suppress-common-lines point-v1.txt point-v2.txt |
実行結果
context形式の出力を表示
(-cオプション)
diffコマンドの出力形式にはいくつか別の形式があります。-cオプションを用いるとcontext形式の出力でファイルの差分を表示することができます。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$diff -c point-v1.txt point-v2.txt *** point-v1.txt 2018-03-26 12:35:47.313785000 +0900 --- point-v2.txt 2018-03-26 12:36:20.122181001 +0900 *************** *** 1,4 **** ! number,name,point 1,aaa,100 ! 2,ccc,80 ! 3,eee,90 --- 1,6 ---- ! no.,name,point 1,aaa,100 ! 2,bbb,28 ! 3,ccc,80 ! 4,ddd,65 ! 5,eee,90 |
context形式の最初の2行は、ファイルを示す記号・ファイル名・修正時間(mtime)を表します。
1 2 |
*** point-v1.txt 2018-03-26 12:35:47.313785000 +0900 --- point-v2.txt 2018-03-26 12:36:20.122181001 +0900 |
そのあとの行に、変更がある行番号と差分を示す行を表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
*************** *** 1,4 **** ! number,name,point 1,aaa,100 ! 2,ccc,80 ! 3,eee,90 --- 1,6 ---- ! no.,name,point 1,aaa,100 ! 2,bbb,28 ! 3,ccc,80 ! 4,ddd,65 ! 5,eee,90 |
変更に関する記号は3つあり、
変更を表す記号が'!'
追加を表す記号が'+'
削除を表す記号が'-'
になります。
追加と削除の記号についての実行結果は以下のようになります。
実行結果とコマンド例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$diff -c point-v1.txt point-v1-add.txt *** point-v1.txt 2018-03-26 12:35:47.313785000 +0900 --- point-v1-add.txt 2018-03-26 12:50:43.437623001 +0900 *************** *** 2,4 **** --- 2,5 ---- 1,aaa,100 2,ccc,80 3,eee,90 + 4,fff,34 $diff -c point-v1.txt point-v1-delete.txt *** point-v1.txt 2018-03-26 12:35:47.313785000 +0900 --- point-v1-delete.txt 2018-03-26 12:50:55.007405000 +0900 *************** *** 1,4 **** number,name,point 1,aaa,100 - 2,ccc,80 3,eee,90 --- 1,3 ---- |
unified形式の出力の表示
(-uオプション)
-uオプションはunified形式の差分の出力を表示します。unified形式はGitでの差分でよく見る形式になります。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$diff -u point-v1.txt point-v2.txt --- point-v1.txt 2018-03-26 12:35:47.313785000 +0900 +++ point-v2.txt 2018-03-26 12:36:20.122181001 +0900 @@ -1,4 +1,6 @@ -number,name,point +no.,name,point 1,aaa,100 -2,ccc,80 -3,eee,90 +2,bbb,28 +3,ccc,80 +4,ddd,65 +5,eee,90 |
unified形式の最初の2行は、ファイルを示す記号・ファイル名・修正時間(mtime)を表します。
1 2 |
--- point-v1.txt 2018-03-26 12:35:47.313785000 +0900 +++ point-v2.txt 2018-03-26 12:36:20.122181001 +0900 |
その次にそれぞれのファイルの変更のある行の範囲が表示されます。
1 |
@@ -1,4 +1,6 @@ |
更にその次の行にそれぞれのファイルの行が表示されます。
1 2 3 4 5 6 7 8 9 |
-number,name,point +no.,name,point 1,aaa,100 -2,ccc,80 -3,eee,90 +2,bbb,28 +3,ccc,80 +4,ddd,65 +5,eee,90 |
行の最初にある文字が、'-'ならば引数の左のファイルの行、'+'ならば引数の右のファイルの行、何もない場合は共通行を表します。
ディレクトリ内のファイル比較
(オプションなし)
diffコマンドでディレクトリを指定するとディレクトリ内のファイルで、同じ名前のファイルの差分を表示します。
また、オプションがない場合は、ディレクトリ内のサブディレクトリの中までは、比較を行いません。サブディレクトリも比較したい場合は-rオプションを使用します。
v1のディレクトリ
1 2 3 4 5 6 |
v1 ├── README ├── point-v1.txt ├── point.txt -> point-v1.txt └── subdir └── subdir_file.txt |
v2のディレクトリ
1 2 3 4 5 6 |
v2 ├── README ├── point-v2.txt ├── point.txt -> point-v2.txt └── subdir └── subdir_file.txt |
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$diff v1 v2 diff v1/README v2/README 1c1 < v1 point table directory --- > v2 README directory Only in v1: point-v1.txt Only in v2: point-v2.txt diff v1/point.txt v2/point.txt 1c1 < number,name,point --- > no.,name,point 3,4c3,6 < 2,ccc,80 < 3,eee,90 --- > 2,bbb,28 > 3,ccc,80 > 4,ddd,65 > 5,eee,90 Common subdirectories: v1/subdir and v2/subdir |
シンボリックリンクをたどりたくない場合は、--no-dereferenceオプションを使用します。この場合は、シンボリックリンク先が同じ名前のファイルの場合に等しい判定になります。
1 2 3 4 5 6 7 8 9 10 |
$diff --no-dereference v1 v2 diff --no-dereference v1/README v2/README 1c1 < v1 point table directory --- > v2 README directory Only in v1: point-v1.txt Only in v2: point-v2.txt Symbolic links v1/point.txt and v2/point.txt differ Common subdirectories: v1/subdir and v2/subdir |
再帰的なディレクトリの比較
(-rオプション)
-rオプションを用いると、ディレクトリを比較したときにディレクトリの内のサブディレクトリも含めて、ファイルを比較し、差分を表示できます。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$diff -r --no-dereference v1 v2 diff -r --no-dereference v1/README v2/README 1c1 < v1 point table directory --- > v2 README directory Only in v1: point-v1.txt Only in v2: point-v2.txt Symbolic links v1/point.txt and v2/point.txt differ diff -r --no-dereference v1/subdir/subdir_file.txt v2/subdir/subdir_file.txt 1c1 < v1 subdir file --- > v2 subdir file |
ファイルが異なるがどうかの判定
(-qオプション)
-qオプション(--briefオプション)は、単純にファイルが同じ内容か違う内容かということを表示します。
コマンド例と実行結果
1 2 |
$diff -q point-v1.txt point-v2.txt Files point-v1.txt and point-v2.txt differ |
ディレクトリ同士を比較したときの方が、よりこのオプションの効果が分かりやすいです。
コマンド例と実行結果
1 2 3 4 5 6 |
$diff -q v1/ v2/ Files v1/README and v2/README differ Only in v1/: point-v1.txt Only in v2/: point-v2.txt Files v1/point.txt and v2/point.txt differ Common subdirectories: v1/subdir and v2/subdir |
差分箇所のC言語での関数名を表示
(-pオプション)
-pオプションを用いると、context形式の出力で差分個所のC言語での関数名を表示できます。関数名の表示については、Cのソースコードに対してインデントされていることが前提になります。また、差分個所の近くに関数名が存在する場合は表示を行いません。
hello.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int rand1; int rand2; int i; srand(time(NULL)); rand1 = rand() % 10; rand2 = rand() % 10; if(rand1 >= 5){ for(i = rand2; i>0; i--){ printf("hello"); } } return 0; } |
hello2.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int rand1; int rand2; int i; srand(time(NULL)); rand1 = rand() % 10; rand2 = rand() % 10; if(rand1 >= 5){ for(i = rand2; i>0; i--){ printf("hellon"); } } return 0; } |
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$diff -p hello.c hello2.c *** hello.c 2018-03-27 21:46:37.349872000 +0900 --- hello2.c 2018-03-27 21:47:36.547455999 +0900 *************** int main() *** 14,20 **** if(rand1 >= 5){ for(i = rand2; i>0; i--){ ! printf("hello"); } } return 0; --- 14,20 ---- if(rand1 >= 5){ for(i = rand2; i>0; i--){ ! printf("hellon"); } } return 0; |
-pオプションの結果として、ファイル名と差分の仕切りになっている文字列の後に
1 |
*************** int main() |
のように関数名が表示されます。この-pオプションはunified形式でも使用できます。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 |
$diff -u -p hello.c hello2.c --- hello.c 2018-03-27 21:46:37.349872000 +0900 +++ hello2.c 2018-03-27 21:47:36.547455999 +0900 @@ -14,7 +14,7 @@ int main() if(rand1 >= 5){ for(i = rand2; i>0; i--){ - printf("hello"); + printf("hellon"); } } return 0; |
unified形式の場合は
1 |
@@ -14,7 +14,7 @@ int main() |
のように行の範囲の後に関数名が表示されます。
差分個所の関数名表示の正規表現を指定
(-Fオプション)
-Fオプションは-pオプションで表示した関数名の表示部分をgrepコマンドの正規表現と同じように指定することができます。指定した正規表現で表示される文字列はcontext形式またはunified形式で利用できます。
また、-pオプションは形式が指定されていない場合は、-c -F '^[[:alpha:]$_]'と同じ意味になります。
つまり、セクションが分かれるようなテキストファイルは、-Fオプションを用いることで差分の箇所のヒントを表示できます。
例えば、rubyでのソースコードで関数名についてのヒントを表示したい場合は、表示したい場所(ここでは、defの宣言がある場所)を指定するように正規表現を指定します。
hello.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def hello prng = Random.new var1 = prng.rand(9) var2 = prng.rand(9) str = 'hello' if var1 >= 5 var2.times do print str end end end hello |
hello2.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def hello prng = Random.new var1 = prng.rand(9) var2 = prng.rand(9) str = 'hello' if var1 >= 5 var2.times do puts str end end end hello |
コマンド例と実行結果(context形式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$diff -c -F'^[[:space:]]*def' hello.rb hello2.rb *** hello.rb 2018-03-27 21:48:17.708025999 +0900 --- hello2.rb 2018-03-27 21:48:41.776054000 +0900 *************** def hello *** 6,12 **** if var1 >= 5 var2.times do ! print str end end end --- 6,12 ---- if var1 >= 5 var2.times do ! puts str end end end |
コマンド例と実行結果(unified形式)
1 2 3 4 5 6 7 8 9 10 11 12 |
$diff -u -F'^[[:space:]]*def' hello.rb hello2.rb --- hello.rb 2018-03-27 21:48:17.708025999 +0900 +++ hello2.rb 2018-03-27 21:48:41.776054000 +0900 @@ -6,7 +6,7 @@ def hello if var1 >= 5 var2.times do - print str + puts str end end end |
ここでの
-F'^[[:space:]]*def'
の意味として、^が行の始めを示し、[[:space:]]の文字クラスはスペースやタブを表します。
正規表現として表すところは、行の始めから0回以上のスペースやタブ文字の後に'def'という文字がある行になります。つまり、インデントがされているかもしれないdefの宣言箇所の行をヒントとして表示する意図があります。
ファイルをパッチ
diffコマンドの内容を保存することでパッチファイルを作成できます。diffコマンドの基本的な構文は
diff [option]... <from-file> <to-file>
のように表すことができます。diffコマンドの出力をファイルに保存すると、from-fileからto-fileへのパッチファイルを作成できます。
このパッチファイルはpatchコマンドを用いると元のファイル(from-file)から差分を取ったファイル(to-file)を作成できます。patchコマンドは
patch [option]... <from-file> <patchfile>
のように使用できます。
コマンド例と実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$diff point-v1.txt point-v2.txt > point.patch $mkdir patchtest $cp point-v1.txt patchtest/ $cp point.patch patchtest/ $cd patchtest/ $ $patch point-v1.txt point.patch patching file point-v1.txt $diff -y -W50 point-v1.txt ../point-v2.txt no.,name,point no.,name,point 1,aaa,100 1,aaa,100 2,bbb,28 2,bbb,28 3,ccc,80 3,ccc,80 4,ddd,65 4,ddd,65 5,eee,90 5,eee,90 |
diffコマンドとpatchコマンドを組み合わせることで、簡単にバージョン間の差異を修正することができます。diffコマンドとpatchコマンドはとても便利なので、セットで覚えておいて損はないコマンドになります。