📜

git blob の内容がバイナリかテキストファイルかを判定したい

2021/05/14に公開
2

2021/07/13 追記

こちらコメントで教えてもらった方法ですが、こちらのほうが単純で分かりやすいです。

$ git cat-file -p <blob sha-1> | file -
/dev/stdin: Mach-O 64-bit executable x86_64

以下はこの方法が思い浮かばなかった当時苦労して編み出した方法となります。
参考までに残しておきます。


tl;dr

[ "$(git cat-file -p <blob sha-1> | od -An -tx1 -N8000 | grep "00")" != "" ] && echo "binaly" || echo "text"

前提

git cat-file -p <blob sha-1> で該当 blob オブジェクトの中身が閲覧できる...のですが、下手に開くとバイナリファイルでコマンドラインが荒れる羽目になります (体験談)
事前に blob オブジェクトの内容がバイナリなのかテキストなのかを知っておきたいですね。
通常のファイルであれば file コマンドを使って判定ができます。
https://qiita.com/kozy4324/items/8c327c6aad64cd820d29
ですが、今回参照したファイルは既に削除済みなため (だから blobオブジェクトを参照している)、残念ながらこの手法は使えないとします。

解決策

od コマンドを使用してバイナリファイルを読み込み、8,000バイト以内に NUL (\00) が出力されていないかで判断することにしました。その結果が上記 #tl;dr のものとなります。
なぜ8,000バイトなのかは下記記事で解説されています。
https://qiita.com/okuoku/items/a21bfa68570ca67817ac
さてコードですが3項演算子部分はどうでもいいとして、以下の部分について簡単に説明していきたいと思います。

git cat-file -p <blob sha-1> | od -An -tx1 -N8000 | grep "00"
  • git cat-file -p <blob sha-1>: blobオブジェクトの中身確認
  • od -An -tx1: 一旦バイナリファイルとして読み込み
    • -An: 行頭のアドレス表示を削除
    • -tx1: 16進数 1バイト単位で出力
    • -N8000: 先頭8,000バイト分を読み込み
  • grep "00": NUL がある行だけを抽出

上記によってNULのある行が検知された場合バイナリファイルと判定しています。

さいごに

短いですが以上となります。
なんかもっとクレバーな策があるかもしれませんが、とりあえず解決できたので良しとします。
これを応用すればshellのpipe入力の内容がバイナリなのかの判定も可能です。意外と応用が効くので覚えておくと役に立つかもしれません。

そもそもなんでこんな作業をしているかというと、実は現在巨大なプロジェクトの肥大化しすぎたリポジトリの容量削減を行っており、不要なblobオブジェクトを洗い出ししている最中だったりします。
巨大化の原因は大抵バイナリファイルをgit管理化に置いているせいで、それの洗い出しに今回の判定が必要だったからです。
リポジトリ容量削減についてはまた後日記事にできたらなと考えております。

ご閲覧ありがとうございました。

Discussion

Yoichi NakayamaYoichi Nakayama

git の判定ロジックを再現してしているのが興味深かったです。

通常のファイルであれば file コマンドを使って判定ができます。

file コマンドでファイル名に - を指定すれば標準入力を読み込むので、 | file - とする手もありますね。

RentalCatRentalCat

コメントありがとうございます!

こちら手元で確認したところおっしゃるやり方でうまくいきました!

$ git cat-file -p <blob sha-1> | file -
/dev/stdin: Mach-O 64-bit executable x86_64

こちらのほうが数倍分かりやすいですね!参考になりました m(_ _)m