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進数 | 意味 |
|---|---|---|---|
| TAB | 11 | 09 | 水平タブ |
| LF | 12 | 0A | 改行 |
| CR | 15 | 0D | 復帰 |
| SPACE | 40 | 20 | 半角スペース |
例えば、
4 11 15
であれば、4バイト目が次のように異なっている。
file1.txt:TABfile2.txt:CR
また、最後の
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.....
各部分の意味は次のとおり。
00000000:先頭からのオフセット(16進数)6162 63:文字列abc09 09 09:TABが3つ0d0a:CRLF(改行)
右側には文字として解釈できる部分が表示されますが、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
主に次のような表記で表示される。
| 表示 | 意味 |
|---|---|
^I | TAB |
^M | CR |
$ | 行末(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 -vet | TABや改行などの不可視文字を表示 |
通常のソースコードや文章の比較であれば diff が便利ですが、改行コードや制御文字まで確認したい場合は、cmp や xxd が適している。
SHA-256のハッシュ値でファイルを比較する
ファイルの同一性を確認する方法として、ハッシュ値を比較する方法もある。
macOSでは、次のコマンドを使用できる。
shasum -a 256 file1.txt file2.txt
両方のファイルのSHA-256ハッシュ値が表示される。
ハッシュ値が一致していれば、実用上、同一の内容であると判断できる。
ただし、ハッシュ値の比較では、ファイルのどこが違うのかまでは分からない。
また、ハッシュ値の一致は数学的に完全な同一性の証明ではありません。手元にある2つのファイルを厳密に比較する目的なら、cmp の方が直接的。
まとめ
今回調べるきっかけになったのは、テキストファイルを複製し、エディタで開いて保存しただけで、元のファイルと一致しなくなったためだった。
調査したところ、元のファイルには行末にTABが含まれていましたが、保存後のファイルではそのTABが削除されていた。
テキストエディタには、保存時にファイルの内容を自動的に整形する機能が備わっていることがある。
代表的なものは以下のとおり。
- 行末のスペースやTABを削除する
- ファイル末尾に改行を追加する
- CRLFとLFなどの改行コードを変換する
- 文字コードを変更する
- BOMを追加・削除する
こうした機能は、通常のプログラミングでは便利。
例えば、ソースコードの行末に不要な空白が残るのを防いだり、ファイル末尾の改行を統一することが可能。
しかし、ファイル形式によっては、TABや空白、改行にも意味がある場合がある。
そのため、見た目が同じでも、エディタで保存する前後でデータが変化している可能性があることには注意が必要。
元の内容を厳密に維持したい場合は、エディタの自動整形設定を確認するか、読み取り専用で扱うと安全。