Shell Scriptにバイナリ(例:tarball)を埋め込み、実行時にバイナリを取り出す方法

前書き:スクリプトサイズが大きい理由

プロプラエタリソフト(例:商用ソフト)のShell Scriptインストーラのサイズを見たら、数百MBだった事はありませんか?

そのような場合は、.deb/.rpmパッケージやtarball等のバイナリがShell Scriptに埋め込まれている可能性が高いです。このようなインストーラは、実行時にバイナリ部分だけを取り出してから、バイナリを操作します。

上記の作りにする理由は、インストーラを単一スクリプト(一つのファイル)にしたいからでしょう。インストール手順がシンプルになりますし、ユーザが操作ミスする可能性も減ります。また、”$ wget $INSTALLER_URL | sh”という形式でインストールもできます(複数のURLに対してwgetする必要がなくなります)。

本記事では、Shell Scriptにバイナリを埋め込み、実行時にバイナリを取り出す方法を紹介します。

            

検証環境

Ubuntu 21.04、Bash 5.1.4、tar (GNU tar) 1.34を使用します。

                  

検証用のtarball作成

Shell Script(test.sh)を含むbinディレクトリを圧縮して、tarballを作成します。tarballの中に、他のファイル(例:パッケージや画像)を含んでも問題ありません。

                  

tarballを埋め込んだShell Scriptの作り方

Shell Scriptの末尾にバイナリを埋め込み、バイナリを実行時に取り出す考え方は、以下の通りです。

  • 編集時:Shell Scriptの末尾に、tarball開始位置の目印(検索用マーカ)を付与
  • 編集時:Shell Scriptの末尾(検索用マーカの後)に、tarballを連結(結合)
  • 実行時:検索用マーカの次の行からShell Script末尾までを抽出(抽出した内容=tarball)

以下、Shell Script(embed.sh)の例です。

embed.shのextractTarball()内では、tarballの内容をbase64コマンドでデコードしています。デコードする事が重要ではなく、tarballをbase64エンコード(英数字、記号2つ、パディングによる表現)でShell Scriptに埋め込む事が目的です。

以下、base64エンコードでtarballをShell Scriptに埋め込む例です。

base64エンコーディングされていない状態、すなわちtarballをShell Scriptにそのまま連結した場合は、Shell Scriptの編集が出来なくなります。正確には、編集後に保存できますが、実行時に以下のエラーが出ます(保存時にtarball部分が意図しない形式に変換され、展開に失敗してしまう)。

また、Shell Scriptを編集していなくても、base64エンコーディングしていない場合はBashがtarballに含まれるNULL文字を省略する可能性があります(”warning: command substitution: ignored null byte in input”がtarball展開時に表示される事があります)。

NULL文字が省略された場合、tarball展開時にチェックサムチェックエラーが発生し、処理が継続できなくなります。

                    

tarball結合後のShell Script実行例

embed.shは、tarballを/tmp以下に展開後、/tmp/bin/test.shを実行します。test.shは”Hello World”を表示し、終了します。

         

おまけ:Shell Scriptをバイナリ化する方法

shc(Shell Script Compiler)でスクリプトをバイナリ化(暗号化)する方法

               

おすすめ