Macでファイルを比較する|どこが変わったかを端末で確かめる

Macでファイルを比較する作業が面倒に感じるのは、道具が足りないからではありません。標準で入っている道具が何を比べるために作られているかを知らないまま、目で見比べる作業に戻ってしまうからです。比べたい対象は、テキストの中身、バイナリのバイト列、フォルダの構成、控えとの差分の4つに分かれます。この4つに道具を割り当てておけば、比較のたびに探し物をしなくなります。

比べたい対象を4つに分けて道具を割り当てる

テキストの中身を行単位で見たいなら diff です。マニュアルは、このコマンドが2つのファイルの内容を比べ、一方を他方に変換するために必要な変更の一覧を標準出力へ書き出すもので、ファイルが同一なら何も出力しないと定義しています。同一なら無言という性質は、そのまま確認の道具になります。

バイナリの中身を1バイトずつ見たいなら cmp です。マニュアルによれば、種類を問わず2つのファイルを比べ、既定では同一なら無言、違えば最初の差が出たバイト番号と行番号を報告します。バイトと行の番号は1から数えられます。差の有無ではなく「どこから違うか」が知りたいときの道具です。

フォルダの構成を比べるのも diff の仕事です。マニュアルには、2つのディレクトリを渡す形式と、その形式でだけ使える指定が別に並んでいます。そして控えとの差分には、Time Machineの側に専用の動詞が用意されています。この4つを分けておくと、「比較ツールを探す」という段階が消えます。

テキストは出力形式を選ぶところから始まる

diff のマニュアルは、出力形式の指定が互いに排他的であることを最初に示しています。よく使われる -u は、前後3行の文脈を付けた統合形式を出します。マニュアルの説明では、-c が出す文脈付きの形式と似ていますが、-c と違って変更される行が追加と削除を含めて1つの区画にまとめて現れます。文脈の行数を自分で決めたい場合は -U に数字を添えます。

画面で読むなら -y が向きます。2列に並べて間にマーカーを出す形式で、マニュアルによれば、空白は対応する行が同一、縦棒は対応する行が異なる、左向きの記号は最初のファイルにだけその行がある、右向きの記号は2つ目のファイルにだけあることを示します。列の幅は -W に数字を添えて指定し、既定は130桁です。同じ行まで全部並ぶと読みにくい場合は --suppress-common-lines を足すと、共通の行が出なくなります。

差があるかどうかだけを知りたい場面もあります。-q は違いがあるときに1行だけ出し、変更の一覧は出しません。色を付けたい場合は --color に never、always、auto のいずれかを渡します。マニュアルによれば、auto は出力が端末で、かつ色の対応を示す環境変数が空でない場合に色を使います。追加が緑、削除が赤になります。

目で見て同じなのに差だと言われるとき

比較の結果が期待と合わない原因の多くは、目に見えない文字です。diff にはその種類ごとに指定があります。

-b は、行末の空白とタブを無視し、そのほかの空白の連続を同じものとして扱います。-w はさらに強く、空白とタブを完全に無視します。マニュアルの例では、if ( a == b ) と if(a==b) が同一として比べられます。整形の設定を変えたあとの差分を見るときに効きます。

改行のコードが混ざっている場合は --strip-trailing-cr を付けると、入力のファイルから復帰の文字を外して比べます。WindowsやWebから受け取ったファイルを手元のものと比べるときは、これが入っているかどうかで結果が変わります。空行だけの塊を無視したいなら -B、大文字と小文字の違いを無視したいなら -i です。マニュアルによれば -i では A と a が同一として比べられます。

日付やバージョンの行だけが毎回違うという場合は -I にパターンを渡します。マニュアルは、拡張正規表現に一致する行の変更、挿入、削除を無視すると説明し、その変更の中のすべての行がいずれかのパターンに一致した場合にだけ無視されるという条件も示しています。見た目の崩れが原因のときは、タブの扱いも見どころです。出力のタブ幅は --tabsize で指定し、既定は8スペース分です。

フォルダごと比べて、反映漏れを構造で見つける

2つのディレクトリを渡す形式には、ファイル同士の比較には無い指定が並びます。-r は共通のサブディレクトリに対して再帰的に比較を当てます。-q と組み合わせると、どのファイルが違うかの一覧だけが出るため、公開前の反映漏れを探す用途に向きます。

片側にだけ存在するファイルの扱いも決められます。-N は、片方のディレクトリにしか無いファイルを、もう一方にも大きさゼロで存在するものとして扱います。-P は2つ目のディレクトリにだけあるファイルについて同じ扱いをします。新しく追加されたファイルの中身まで出したい場合は -N、追加を見つけたいだけなら付けない、という使い分けになります。

同じだったファイルも一覧に出したい場合は -s を付けると、そのままでは触れられないファイルも報告されます。対象から外したいものは -x にパターンを渡し、パターンをまとめたファイルがあるなら -X で渡します。マニュアルによれば、パターンはシェルと同じ形式で名前の部分に対して照合されます。ビルドの出力やキャッシュを外してから比べる形にすると、出力が読める量に収まります。大きなディレクトリで途中から再開したい場合は -S にファイル名を渡します。

バイナリと画像は、バイト単位に降りる

diff は、ファイルにバイナリの文字が含まれていると、通常は違いがあるという1行だけを出して中身を出しません。マニュアルは、-a を付けるとすべてのファイルをテキストとして扱い、差分の出力を強制すると説明しています。画像や書庫に -a を当てても読める形にはならないため、ここで cmp へ移ります。

cmp の出力は目的別に選べます。-l はすべての差について、バイト番号を10進で、異なるバイトの値を8進で出します。-x は同じ内容を16進で出し、最初のバイトを0として数えます。-b は差が見つかったバイトそのものを表示します。最後まで見る必要がない場合は -n に数を渡して比べる範囲を絞り、先頭の決まった長さを飛ばしたい場合は -i に数を渡します。

書き出しの設定を変えて保存し直した画像が本当に同じかを確かめたい場面では、-z が効きます。マニュアルによれば、通常のファイルについて先にサイズを比べ、等しくなければその時点で比較を失敗させます。同一かどうかだけを機械に判定させたい場合は -s を付けると、何も出力せず終了状態だけを返します。

比較の道具を対象ごとに分けて用意すると、別の問題が起きます。

17年Webデザイナー・コーダーをやってきた中で、ファイル比較で事故った経験は数え切れません。テキスト比較ツール、画像比較ツール、フォルダ比較ツールをそれぞれ別々に使っていた時期が長かったです。でも結局、「使う頻度が低いツールは存在を忘れる」という人間の認知特性に勝てませんでした。 出典: rkpg.net

忘れるという問題への対処は2通りあります。1つは入り口を1本にまとめること、もう1つは標準で入っている道具だけで足りる形にしておくことです。diff と cmp はどのMacにも入っているため、後者を選ぶと、環境を移ったときにも同じ手が使えます。

Gitの管理下でなくても git の差分表示が使える

読みやすさの面では、Gitの差分表示に慣れている人が多くなっています。git diff のマニュアルには、管理外のファイルを比べる形式が載っています。--no-index を付けてパスを2つ渡す形式で、説明には、ファイルシステム上の指定した2つのパスを比べる形式だと書かれています。

省略できる条件も明記されています。Gitが管理している作業ツリーの中で走らせ、かつ少なくとも一方のパスが作業ツリーの外を指している場合、またはGitが管理している作業ツリーの外で走らせた場合は --no-index を省略できます。さらに、この形式は --exit-code を含むため、差の有無が終了状態に出ます。語単位の色分けや、慣れた読み方をそのまま使えるのが利点です。

画面上で並べて確かめたい場合の選択肢もあります。opendiff のマニュアルは、これがFileMergeをターミナルから起動して、ファイルまたはディレクトリを図で比べたり統合したりするための道具だと説明しています。-ancestor に共通の祖先を渡すと3者の比較になり、同じ元のファイルを2人が別々に編集した場合に使えると書かれています。統合した内容は -merge で指定した先に保存でき、指定しない場合は保存時に尋ねられます。すでにFileMergeが動いているときは、その動いているものに繋がります。なお、このコマンドとFileMergeはXcodeの開発ツールの一部として入ります。

控えとの差分と、終了状態を使った確認

いま手元にあるファイルと、控えの中のファイルを比べたい場合は、Time Machineの側に専用の動詞があります。tmutil のマニュアルは、compare をバックアップの差分を取る動詞だと定義し、引数が無ければ機械と最新のバックアップを比べ、バックアップのパスを1つ渡せばそのバックアップと比べ、パスを2つ渡せばその2つを比べると説明しています。比べる属性を指定しない場合の既定の組は -@gmstu で、-a を指定するとサポートしているすべてのメタデータを比べます。

比較を人の目で読む作業から、機械が判定する作業に変えられるのが終了状態です。diff のマニュアルには、差が見つからなければ0、差が見つかれば1、エラーが起きた場合は1より大きい値で終了すると書かれています。cmp で -s を付けた場合も、出力を出さずに終了状態だけを返します。

この性質があると、確認を手順書から外して仕組みに変えられます。書き出しの前後で同じファイルを比べ、差が出たときだけ人が読む形にしておけば、毎回目で見比べる必要がなくなります。比較の道具を選ぶときに終了状態を見ておくと、その道具を自動の確認に組み込めるかどうかが先に分かります。

出力を読む窓と、対象を選ぶ窓が分かれている

ここまで並べた指定は、覚える量としては多くありません。それでも比較が面倒に感じるのは、対象を選ぶ窓と、結果を読む窓が分かれているからです。フォルダの一覧で2つのファイルを選び、パスを端末へ運び、出力を読み、どちらを残すかをまたフォルダの一覧で決める、という往復が挟まります。

数えてみると、打つコマンドの数より、窓を切り替えてパスを運ぶ回数のほうが多くなります。深い階層のパスを手で打ち直すと、1回あたり数秒が消え、打ち間違えると存在しないファイルを比べたことになります。差分の読み方をAIに尋ねる場合は窓がもう1つ増えます。フォルダとターミナルとAIが同じ窓にある状態なら、一覧で選んだ2つのファイルをそのまま比較の引数として渡し、出力を同じ画面で読み、残す側をその場で決められます。

ターミナルを内蔵したファイル管理で何がどこまでできるのかはできることに一覧があり、標準のFinderやほかのアプリとの違いは他のファイル管理との比較に整理されています。出先から自宅のMacの状態を確かめる使い方はiPhone・iPadから続きをが該当し、日本語以外の表示については対応言語、費用の考え方は料金、導入前に迷いやすい点はよくある質問にまとめられています。

Macでファイルを比較する作業は、対象を4つに分け、それぞれの入り口を1つ決めた時点でほぼ終わります。テキストは diff、バイト列は cmp、構成は diff のディレクトリ形式、控えとの差は tmutil の差分です。あとは空白と改行の指定を覚えておけば、目で見比べる作業に戻る場面はほとんど残りません。

よくある質問

Macに比較のためのアプリを入れる必要はありますか?

テキストとバイナリ、フォルダの比較は標準で入っている diff と cmp で足ります。図で並べて統合まで行いたい場合は、opendiff から起動するFileMergeが選択肢になりますが、これはXcodeの開発ツールの一部として入るものです。Gitを使っているなら、管理外のファイルにも git diff --no-index が使えます。

目で見て同じ内容なのに差があると出ます。原因は何ですか?

空白、タブ、改行のコードが原因になりやすいです。行末の空白や空白の連続を無視するなら -b、空白とタブを完全に無視するなら -w、復帰の文字を外して比べるなら --strip-trailing-cr を付けます。日付やバージョンの行だけが毎回違う場合は、-I にその行に一致するパターンを渡すとその変更を無視できます。

画像や書庫のようなファイルはどう比べればよいですか?

diff はバイナリの文字を含むファイルについて、違いがあるという1行だけを出して中身を出しません。cmp に移ると、最初に差が出たバイト番号が分かり、-l でバイト番号と値の一覧、-x で16進の表示になります。同一かどうかだけ知りたい場合は -s を付けて終了状態で判定します。

フォルダ2つの反映漏れを探すにはどうしますか?

diff にディレクトリを2つ渡し、-r で再帰的に、-q で違いのあるファイルの一覧だけを出す形が扱いやすくなります。片側にしか無いファイルの中身も見たい場合は -N を足します。ビルドの出力やキャッシュは -x にパターンを渡して外すと、出力が読める量に収まります。

記事一覧へ戻る