cmp入門|バイナリの違いをバイト単位で見つける

cmp入門|バイナリの違いをバイト単位で見つける

配置したファイルが手元のものと本当に同じか、後から確かめたくなることがあります。

テキストならdiffで行の差分を読めばいいのですが、画像や実行ファイルが相手だと「Binary files x.bin and y.bin differ」の一行で終わってしまう。

違うことは分かっても、どこがどう違うのかは教えてもらえません。

そこで出番になるのがcmpです。

2つのファイルが最初に食い違う位置を、バイト単位で指してくれるコマンド

以下の実行例は、手元の GNU diffutils 3.10(Git for Windows の bash 5.2.37)で実際に動かした出力をそのまま載せています。

最初に違うバイトだけを報告する

cmpの基本形は、比べたい2つのファイルを並べるだけ。

$ cmp a.txt b.txt
a.txt b.txt differ: char 14, line 3

14バイト目、行でいうと3行目で食い違った、という意味になります。

差分の一覧ではなく最初の1箇所だけを報告して打ち切るのが、diffとの一番の違い。

内容が同じなら、何も言わずに終わります。

$ cmp a.txt d.txt
$ echo $?
0

無言で終わったら一致、というのがcmpの作法です。

片方が途中で終わっているときは、専用のメッセージに変わります。

$ cmp a.txt c.txt
cmp: EOF on c.txt after byte 13, line 2

先頭から13バイトは完全に同じで、そこで c.txt のほうが尽きたという報告。

転送やコピーが途中で切れたファイルは、この形で見分けられます。

終了コードで自動化に組み込む

現場で効いてくるのは、画面に出るメッセージよりも終了コードのほうでした。

終了コードは3つに分かれています。

  • 0:内容が同じ
  • 1:内容が違う
  • 2:ファイルが開けないなどのエラー

「違う」と「読めなかった」が別の値になっているのが大事なところ。

$ cmp a.txt nosuch.txt
cmp: nosuch.txt: No such file or directory
$ echo $?
2

比較に失敗したのか本当に中身が違うのかを取り違えないので、スクリプトの分岐に安心して置けます。

メッセージが邪魔なときは-sを付けると黙ってくれる。

$ if cmp -s x.bin y.bin; then
>   echo "同一なので何もしない"
> else
>   echo "差分あり、配置する"
> fi
差分あり、配置する

変わったときだけ配置するという条件分岐が、これだけで書けてしまいます。

違いを掘り下げるオプション

最初の1箇所では足りないとき、-lを付けると食い違うバイトを全部並べてくれる。

$ cmp -l a.txt b.txt
14 143 103
15 150 110
16 145 105
17 162 122
18 162 122
19 171 131

左から順に、バイト位置・左のファイルの値・右のファイルの値という並び。

値が8進数で出るので、143 は小文字の c、103 は大文字の C にあたります。

8進数のままだと読み解くのが面倒なので、文字も一緒に見たいときは-bが近道。

$ cmp -b a.txt b.txt
a.txt b.txt differ: byte 14, line 3 is 143 c 103 C

ヘッダやタイムスタンプだけが違うファイルでは、先頭を読み飛ばす-iが効きます。

$ cmp -i 2001 x.bin y.bin
$ echo $?
0

先頭2001バイトを両方とも飛ばせば、残りは完全に一致していると確かめられました。

逆に先頭だけ見たいなら、-nで比較する長さを区切る。

オプションの正確な一覧は、GNU diffutils マニュアルの Invoking cmpにまとまっています。

NO IMAGE

sha256sum・diff との役割分担

同一性を確かめるだけならsha256sumという手もある。

$ sha256sum x.bin y.bin
aac74bfbe7b2b8b2...  x.bin
cc060604905ad7d2...  y.bin

ただしハッシュが答えてくれるのは同じか違うかの二択だけで、どこが違うのかまでは分かりません。

ハッシュ値の使いどころは、sha256sum入門|ファイルの中身が同じか確かめるで整理しました。

sha256sum入門|ファイルの中身が同じか確かめる

cmpのほうは位置を指すので、壊れているのが先頭のヘッダなのか末尾なのかまで切り分けられる。

テキストが相手で行の中身を読みたいなら、やはりdiffが向いています。

$ diff x.bin y.bin
Binary files x.bin and y.bin differ

同じか否かはハッシュ、どこが違うかは cmp、何が違うかは diff、と役割で覚えておくと迷いません。

行単位の差分の読み方は、diff入門|2つのファイルの違いを行単位で見つける基本にまとめてあります。

diff入門|2つのファイルの違いを行単位で見つける基本

最後に

cmpは、2つのファイルが最初に食い違う位置を指すだけの小さなコマンドでした。

小さいぶん終了コードとの相性がよく、配置や検証のスクリプトへ素直に組み込めます。

まずはcmp -sで「同じなら何もしない」を書いてみて、追いかける必要が出たら-l-bへ降りていく。

その先でバイトの並びそのものを覗きたくなったらodxxdが次の一手になります。

バイト単位の読み方は、od・xxd入門|ファイルの中身をバイト単位で確かめるが入口になるはず。

od・xxd入門|ファイルの中身をバイト単位で確かめる

「違う気がする」を「14バイト目が違う」に変えられると、調査の足取りはずいぶん軽くなります。

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA