diff コマンドとファイル形式 – コンピュータ活用シリーズ

新規: ㋿ 3年 7月17日 更新: ㋿ 3年 7月17日 著者: 山田 泰司

diff の何が重要か

diff コマンドは二つのファイルの違いを抽出するためのコマンドです。次の2つの用途があります。

  1. 人間がみて2つのファイルの違いを分かりやすく知る
  2. 機械が2つファイルの違いから、
    • 旧版から新版を生成
    • 新版から旧版に戻す

すなわち、人間にも機械にも大変重要なコマンドであります。

人が読めるファイル形式とは

では、人が diff をとる価値が見出せるファイル形式は何か、代表的な拡張子 (と MIME タイプ) を紹介します。馴染みのあるマイクロソフト Word/Excel などについては、まだ登場しませんので、少々辛抱を。

  1. .txt (text/plain) … いわゆるプレーンテキスト=文書
  2. .tex … 技術文書組版ソースファイル=文書ソース
  3. .md (text/markdown) … プレーンテキスト(マークアップ言語への変換向け)=文書
  4. .html (text/html), .xml (text/xml) … マークアップ言語形式=文書
  5. .svg (image/svg+xml) … スケーラブルベクタグラフィックスマークアップ言語形式=イメージ
  6. .css (text/css) … カスケードスタイルシート=文書スタイルソース
  7. .rtf (application/rtf) … リッチテキスト形式=文書
  8. .js (application/javascript) … プログラミング言語 Javascript のコード(実行目的に難読化は .min.js)=実行プログラム
  9. .json (application/json) … プログラミング言語 Javascript 由来のデータ形式=データ
  10. .ics (text/calendar) … カレンダー及びスケジュール管理=データ
  11. .csv (text/csv) … カンマ区切りテキスト=テキストデータ
  12. .py, .rb, .tcl, .pl … 各種スクリプト言語ソース及び実行ファイル=実行プログラム
  13. .c, .cc … プログラミング言語 C/C++ ソースファイル=プログラムソース
  14. .sh (application/x-sh), .bash, .ksh … Unix 向けシェルスクリプトファイル=実行プログラム
  15. .bat … Windows 用バッチファイル=実行プログラム

これらは人が書いて読めることが想定されていますが、それぞれその度合いは異なり、.svg, .rtf などは特に機械が生成する細かな情報の羅列ですし、決して人が読みやすいとは言えません。昨今の .html, .css なども機械が生成したファイルが主流であり、人による読みやすさはあまり重要視されていません。

一方、.txt.md をはじめ、プログラムコードも人間によるメンテナンスの必要性から、人による読みやすさが担保されているファイル形式です。これらは別に圧縮や暗号化されているわけではないので、人が読めるかという問題と、読むのが面倒という問題と、区別がなかなか難しい話でもありますので、それぞれ一般的な用途での説明として捉えてください。

これらのファイルを編集している人にとっては特に、diff コマンドは必須のものとなっています。

diff コマンドの例

端末での操作に慣れ親しんでいる使い手なら、以下のようなコマンドオペレーションで2つのファイルの差異を確認することはよくあります。

% diff -u cursive-fonts.css~ cursive-fonts.css⏎
--- cursive-fonts.css~	2021-07-14 21:21:39.000000000 +0900
+++ cursive-fonts.css	2021-07-14 21:37:48.000000000 +0900
@@ -1,7 +1,7 @@
 @import url("default-fonts.css");
-body {
+body:not(:lang(ja)) {
   font-family: C-cursive, ja-cursive, C-serif, serif;
 }
-html[lang='ja'] > body {
+body:lang(ja) {
   font-family: ja-cursive, C-cursive, ja-serif, C-serif, serif;
 }

端末で、普通はこのような色はつきません。

行頭の「-+」記号で「旧新」の行を表しています。新旧というより、コマンドライン引数で渡した順番が「旧新」になっているので、そのような結果となっています。

この順番はのちの patch コマンドでも習慣になっていますので覚えておきましょう。加えて、diff の -u オプションはどのファイルとの差異なのかが付記されるので、同じく重要になってきます。

さて、この diff コマンドでは、人がファイル全体を眺めて確認するという用途にはあまり向いていません。

diff -y オプション

実は diff コマンドにも全体を見るためのオプションが用意されています。先の例なら、以下のように実行します。

% diff -y -W 128 cursive-fonts.css~ cursive-fonts.css⏎
@import url("default-fonts.css");				@import url("default-fonts.css");
body {							      |	body:not(:lang(ja)) {
  font-family: C-cursive, ja-cursive, C-serif, serif;		  font-family: C-cursive, ja-cursive, C-serif, serif;
}								}
html[lang='ja'] > body {				      |	body:lang(ja) {
  font-family: ja-cursive, C-cursive, ja-serif, C-serif, seri	  font-family: ja-cursive, C-cursive, ja-serif, C-serif, seri
}								}

中央の「|」記号で異なる行が表されています。人によってはこれで十分でしょうけど、お世辞にも視覚的に分かりやすいとは言えません。

視覚的な diff

人が視覚的に2つのファイルの違いを全体的にみて確認します。どんな手段があるでしょうか?

  1. diff コマンドの -y オプション(先述)
  2. エディタの diff – Emacs, Vim の diff モード
  3. GUI 向け diff – Meld (Windows/Linux/macOS 向け)
  4. Web 向け diff – インターネット上の diff サービス
  5. マイクロソフト Word/Excel/PowerPoint – 編集履歴

実のところ、ファイルの違いを視覚化するアプリケーションは他にもたくさんあります。モノによっては画像の差異も視覚化してくれるものもあるようです。

ここでは 3. の Windows, Linux, macOS で動作する Meld オープンソースアプリケーションの実行例を紹介します。

Meld スクリーンショット 00

フォルダごとファイル集合の違いが見れたりと、たいへん便利です。もっとも diff コマンドの -r オプションでフォルダごとの差異は得られますし、そちらが本家です。

GUI 向けにおける大きな違いは「行の中の文字列の差異」までもが配色で分かりやすくなっていることでしょう。

4. のウェブサービスは手元のファイルの差異をみるには情報漏洩の懸念があるなど、まったくお勧めしません。

patch コマンド

diff コマンドの出力ファイルを使った、極めて重要な活用例として patch コマンドが一番にあげられます。以下の端末での操作例で、diff と patch の新旧ファイルの自動生成が説明できます。

% に続く文字列がオペレータが打ち込んだコマンド行です。

% diff -u cursive-fonts.css~ cursive-fonts.css > cursive-fonts.css.patch⏎

% patch --dry-run < cursive-fonts.css.patch⏎
patching file cursive-fonts.css
Reversed (or previously applied) patch detected!  Assume -R? [n] ^C

% patch --dry-run -R < cursive-fonts.css.patch⏎
patching file cursive-fonts.css

端末での操作に不慣れな方に分かりやすく説明します。ここでは3つの操作をしており、順を追って説明します。

  1. diff -u cursive-fonts.css~ cursive-fonts.css > cursive-fonts.css.patch
    diff の出力をリダイレクトでファイル cursive-fonts.css.patch に保存しています。これが「パッチファイル」と呼ばれるものです。このパッチファイルと新旧ファイルのどちらかがあれば、旧新ファイルが自動生成できるというわけです。
    試してみましょう。patch コマンドを --dry-run オプションつきで、実際のファイル書き込みはしない「お試しモード」で動かしてみます。
  2. patch --dry-run < cursive-fonts.css.patch
    このフォルダにある cursive-fonts.css ファイルが旧ファイルなら新ファイルが自動生成されて、元のファイルは cursive-fonts.css.orig に変更されます。
    しかし、この例では新ファイルは既にあるので「もしかして旧版に戻したいのかい?」と尋ねられています。ひとまず、コントロールを押しながら C キーで中断させます。
  3. patch --dry-run -R < cursive-fonts.css.patch
    旧ファイルに戻したいときは、patch コマンドが指南してくれたように、-R オプションをつけて実行します。元のファイル(この例では新ファイル)は cursive-fonts.css.orig に変更されます。

patch コマンドに --dry-run オプションを指定しなければ実際に生成しますので、別のフォルダにコピーしたもので試してみるとよいでしょう。

GUI 再び

さて、Windows 用にマイクロソフト謹製の「WinDiff」がありましたが、あくまで開発者向けに過ぎず、多くの方は「WinMerge」を使っているようです。

一方、macOS 用には開発ツール Xcode に「FileMerge」が(/Applications/Xcode.app/Contents/Applications/FileMerge.app)付属しています。Linux など Unix 用には先の Meld です。

先のパッチ当ての作業が複雑になってくるとグラフィカルに人の目で確認して行いたいという需要もあるものですから、この手のアプリケーションは patch コマンドの機能も兼ねていることが多いです。ゆえに「Merge」という命名で、さまざまなパッチを統合して新しいファイルを生成するツールとなっているわけです。

おわりに

diff コマンドはコンピュータ科学にたいへん重要な役割を果たしています。

ソフトウェア開発のウェブプラットフォームである GitHub の中核技術である分散バージョンコントロールシステム git も自前の diff, patch 互換機能を備えてますし、フリー百科事典の WikiPedia の編集における差分においてもマークダウン文書の diff から得られています。

時代を遡れば、patch の作者はスクリプト言語 Perl のラリー・ウォール氏ですし、diff の作者は Unix 創始者の一人であるダグラス・マキルロイです。1970 年代初頭から使われている技術が半世紀以上に渡って使われ続けていることは驚くべきことです。

論文などを執筆する研究者や技術者は、数式を含む \(\TeX\) 文書の更新履歴を、先のグラフィカルな diff などで誤りがないか慎重に確認します。

コードを書くブログラマは、プログラムは「 」(半角スペース)が「 」(全角スペース)になっていただけで正しくないわけですから、問題があれば動いていた時点のコードと diff などを使って慎重に誤りを探します。

このように、diff コマンドや、それを視覚化する GUI 版の活用は、確認すべきところを最小限にし誤りを人間が見つけやすくするためだけでなく、文書やプログラムコードの更新やバージョン管理にも役立っているのです。

バイナリファイルの差分

本稿ではテキストファイルの差分について取り扱いましたが、バイナリファイルの差分についても古くから開発されており、xdeltaVCDIFF などがあげられます。こちらは人の目に直に触れる対象ではないので、さらに縁の下の力持ちと言えます。

参考文献

  1. diff Manual, POSIX.1-2017 XCU, IEEE and The Open Group, 2001-2018. … diff コマンドを定義するソースコード以外の一次情報です。
  2. patch Manual, POSIX.1-2017 XCU, IEEE and The Open Group, 2001-2018. … patch コマンドを定義するソースコード以外の一次情報です。
  3. Media Types, IANA, 2021. … text/plain などの MIME タイプを管理する IANA (インターネット番号割当機関) による一次情報です。
Copyright © 2021 Taiji Yamada