Skip to content

2つのファイルが完全に同じか確認する方法。cmp・xxdでバイト単位の差分を調べる

Published: at 09:00

Table of contents

Open Table of contents

cmpコマンドでファイルが完全に同じか確認する

2つのファイルの内容が完全に一致するか確認するなら、cmp コマンドが便利。

cmp file1.txt file2.txt

ファイルの内容が完全に同じ場合は、何も表示されない。

異なる場合は、最初に違いが見つかった位置が表示される。

file1.txt file2.txt differ: byte 4, line 1

この例では、1行目の4バイト目に違いがあることを示している。

なお、cmp はファイルの名前や更新日時、アクセス権限などではなく、ファイルの中身をバイト単位で比較する。

比較結果だけを表示する

-s オプションを指定すると、比較結果のメッセージを表示せず、終了ステータスだけを返す。

cmp -s file1.txt file2.txt

終了ステータスは以下のとおりです。

終了ステータス意味
0ファイルの内容が一致
1ファイルの内容が異なる
2以上エラーが発生

シェルの条件分岐を使えば、結果を分かりやすく表示が可能。

if cmp -s file1.txt file2.txt; then
  echo "完全に同じ"
else
  echo "異なる、または比較エラー"
fi

単純に2つのファイルが同一か確認するだけなら、この方法で十分。

cmp -lで異なるバイトの位置と値を確認する

cmp に -l オプションを指定すると、異なるバイトの位置と値を表示が可能。

cmp -l file1.txt file2.txt

例えば、次のような結果が表示されたとする。

4  11  15
5  11  12
cmp: EOF on file2.txt

それぞれの列の意味は下記の通り。

バイト位置  file1の値  file2の値

ここで注意したいのは、cmp -l が表示するバイト値は8進数であるという点。

代表的な制御文字は下記の通り。

文字8進数16進数意味
TAB1109水平タブ
LF120A改行
CR150D復帰
SPACE4020半角スペース

例えば、

4  11  15

であれば、4バイト目が次のように異なっている。

また、最後の

cmp: EOF on file2.txt

は、比較中に file2.txt が先にファイル末尾へ到達したことを意味する。

つまり、file2.txt の方が短いことがわかる。

xxdでファイルのバイト列を確認する

cmp では異なるバイトの位置を調べられるが、ファイルの実際のバイト列を確認するなら xxd を使用する。

xxd は、ファイルの内容を16進数で表示するコマンド。

xxd file1.txt

例えば、abc の後ろにTABが3つ、最後にCRLFが含まれているファイルなら、次のように表示される。

00000000: 6162 6309 0909 0d0a                      abc.....

各部分の意味は次のとおり。

右側には文字として解釈できる部分が表示されますが、TABや改行などの制御文字は通常 . として表示される。

このように、xxd を使えば、テキストエディタ上では見えない制御文字も確認できる。

xxdとdiffを組み合わせて比較する

2つのファイルをそれぞれ16進数で表示し、その差分を確認する方法もある。

xxd file1.txt > file1.hex
xxd file2.txt > file2.hex

diff -u file1.hex file2.hex

ただし、比較用のファイルをわざわざ作る必要はない。

BashやZshでは、プロセス置換を使って次のように実行可能。

diff -u <(xxd file1.txt) <(xxd file2.txt)

この方法なら、一時ファイルを作らずに16進数の差分を確認可能。

なお、xxd の出力は一定のバイト数ごとに区切られるため、途中でバイトが追加・削除されると、それ以降にも大量の差分が表示される場合がある。

正確な差異の位置を調べる場合は cmp -l と併用するとよい。

cat -vetでTABや改行などの不可視文字を確認する

TABや改行などの不可視文字を、人間が読みやすい形で確認したい場合は、cat -vet を使用する。

cat -vet file1.txt

主に次のような表記で表示される。

表示意味
^ITAB
^MCR
$行末(LFの直前など)

例えば、以下のように表示されたとする。

abc^I^I^I^M$

これは、実際には次のバイト列で構成されている。

61 62 63 09 09 09 0d 0a

つまり、abc の後ろにTABが3つあり、CRLFで行が終わっていることを示している。

一方、次のような表示であれば、

abc^M$

実際のバイト列は下記になる。

61 62 63 0d 0a

2つのファイルはテキストエディタ上では同じ abc に見えるかもしれないが、バイト単位では一致していない。

ちなみに、LFのみで改行されている場合は、

abc$

と表示される。

ファイル末尾だけを確認する方法

エディタで開いて保存した後にファイルが変化した場合、行末の空白やTAB、ファイル末尾の改行が原因になっていることがある。

そのような場合は、ファイル全体ではなく、末尾だけを確認すると効率的。

tail -c 30 file1.txt | xxd

tail -c 30 は、ファイルの末尾30バイトを取得するコマンド。

比較対象も同様に確認する。

tail -c 30 file2.txt | xxd

例えば、末尾が

09 09 09 0d 0a

であれば、TABが3つ続いた後、CRLFで終わっている。

一方、

0d 0a

だけなら、最後の改行の直前にTABはない。

このように、末尾の数バイトを確認するだけでも、ファイルが異なる原因を特定できることがある。

diffコマンドとの違い

テキストファイルの比較には、diff コマンドもよく使われる。

diff -u file1.txt file2.txt

diff は行単位で差分を確認でき、変更されたテキストを調べるのに適している。

一方、cmp はバイト単位でファイルを比較する。

コマンド主な用途
diffテキストの変更箇所を行単位で確認
cmpファイルがバイト単位で一致するか確認
cmp -l異なるバイトの位置と値を確認
xxdファイルのバイト列を16進数で表示
cat -vetTABや改行などの不可視文字を表示

通常のソースコードや文章の比較であれば diff が便利ですが、改行コードや制御文字まで確認したい場合は、cmp や xxd が適している。

SHA-256のハッシュ値でファイルを比較する

ファイルの同一性を確認する方法として、ハッシュ値を比較する方法もある。

macOSでは、次のコマンドを使用できる。

shasum -a 256 file1.txt file2.txt

両方のファイルのSHA-256ハッシュ値が表示される。

ハッシュ値が一致していれば、実用上、同一の内容であると判断できる。

ただし、ハッシュ値の比較では、ファイルのどこが違うのかまでは分からない。

また、ハッシュ値の一致は数学的に完全な同一性の証明ではありません。手元にある2つのファイルを厳密に比較する目的なら、cmp の方が直接的。

まとめ

今回調べるきっかけになったのは、テキストファイルを複製し、エディタで開いて保存しただけで、元のファイルと一致しなくなったためだった。

調査したところ、元のファイルには行末にTABが含まれていましたが、保存後のファイルではそのTABが削除されていた。

テキストエディタには、保存時にファイルの内容を自動的に整形する機能が備わっていることがある。

代表的なものは以下のとおり。

こうした機能は、通常のプログラミングでは便利。

例えば、ソースコードの行末に不要な空白が残るのを防いだり、ファイル末尾の改行を統一することが可能。

しかし、ファイル形式によっては、TABや空白、改行にも意味がある場合がある。

そのため、見た目が同じでも、エディタで保存する前後でデータが変化している可能性があることには注意が必要。

元の内容を厳密に維持したい場合は、エディタの自動整形設定を確認するか、読み取り専用で扱うと安全。