2018/03/29

Debianパッケージの汎用的な展開方法

Debianパッケージ(.debファイル)は通常はdpkgという低レベルパッケージマネージャがAPTという高レベルパッケージマネージャやそのGUIを提供するSynapticなどを通して使用し、パッケージの中身を直接取り出したい場合にはdpkg-x [.debファイルの場所] [展開先]オプションを用いるのだが、このパッケージのファイル形式はディストリに依存しないツールを組み合わせることでも展開できるものとなっており、ここではその展開方法についてを扱う。

  1. パッケージ自体の形式
    1. binutilsとarコマンド
    2. .debファイルの直接の中身
  2. 汎用的な展開スクリプト

パッケージ自体の形式

binutilsとarコマンド

.debファイルはbinutilsと呼ばれるツール群の一部であるarというコマンドで扱える一種の書庫ファイルの形式をとっている。

binutilsは、C言語系の言語のプログラムを実行可能な形式に変換する流れの中でコンパイラが中間形式としてアセンブリ言語にしたものを機械語に変換する役割のasというアセンブラを含むため、コンパイラのGCCClangから依存されている。

arコマンドもソフトウェアのビルドの過程で自動的に用いられることがあり、個々のソースファイルからコンパイルされた複数のオブジェクトファイル(.oファイル)群を一つのファイルにまとめた静的リンクライブラリのファイル(.aファイル)として主に使われ、独自の機能を提供する “ライブラリ” の類でそれを用いたプログラムをビルドするのに必要な “開発” パッケージ(ディストリによって異なるが末尾が-devel-devとなる名前)の一部として提供される。

.debファイルの直接の中身

.debファイルは、arで扱える書庫としては以下の3つのファイルから成る。

  • debian-binary: パッケージ形式のバージョンが書かれたテキストファイル
  • control.tar.xz: メタデータ(依存関係などの情報やインストール時に実行されるスクリプトなどを含む)
  • data.tar.xz: パッケージの中身そのもの

以前は2つの書庫ファイルについて.tar.gz形式が用いられていたが、(ビルドされた時期が)新しいパッケージでは圧縮率の高い.tar.xz形式に移行している。

$ ar t /path/to/package.deb
debian-binary
control.tar.xz
data.tar.xz

過渡期のパッケージではcontrol.tar.gzが入っていたりする。

$ ar t /path/to/package.deb
debian-binary
control.tar.gz
data.tar.xz

なお、古めのディストリで新しいディストリ向けのパッケージをインストールしようとするとcontrol.tar.xzが処理できずに失敗することがある。

汎用的な展開スクリプト

引数に.debファイルの場所と追加で展開先ディレクトリ(省略時は現在の作業ディレクトリ)を指定して実行すると、その.debファイルの中身を指定ディレクトリ内に展開する。操作にはパイプを用いているため、一時ファイルは作成しない。

動作にはbinutilsパッケージが必要で、それ以外にもtarXZ Utilsなども用いられるがOSの基本部分として標準でインストールされていることが多いため、ない場合のみインストールする。

[任意]ファイル名:deb-unpacker.sh ライセンス:MIT
#! /bin/sh

# Debian package unpacker
# (C) 2018 kakurasan
# Licensed under MIT

if test -z "${1}"; then
    echo "Usage: ${0} [DEB FILE] ([OUTPUT DIR])"
    exit 1
fi

DEB="${1}"
OUTDIR="${2:-.}"  # Default is the current directory

# Check for binutils
ar --version > /dev/null 2>&1
if test ${?} -ne 0; then
    echo "ERROR: Package \"binutils\" is not installed."
    exit 1
fi

# Check for input file
if ! test -f "${DEB}"; then
    echo "ERROR: File \"${DEB}\" doesn't exist."
    exit 1
fi

# Check whether debian-binary is present
ar t "${DEB}" 2>/dev/null | grep -E ^debian-binary$ >/dev/null
if test ${?} -ne 0; then
    echo "ERROR: File \"${DEB}\" is not Debian package file."
    exit 1
fi

# Create output directory
if ! mkdir -p "${OUTDIR}"; then
    echo "ERROR: Could not create directory \"${OUTDIR}\"."
    exit 1
fi

# Determine the compression program and unpack data.tar.*
DATA_TARBALL="$(ar t "${DEB}" 2> /dev/null | grep data.tar.)"
case "${DATA_TARBALL}" in
    *.tar.xz)
        ar p "${DEB}" data.tar.xz | tar -C "${OUTDIR}" -Jvxf -
        ;;
    *.tar.bz2)
        ar p "${DEB}" data.tar.bz2 | tar -C "${OUTDIR}" -jvxf -
        ;;
    *.tar.gz)
        ar p "${DEB}" data.tar.gz | tar -C "${OUTDIR}" -zvxf -
        ;;
    *)
        echo "ERROR: data.tar.{xz,bz2,gz} not found."
        exit 1
        ;;
esac

# Done
echo "Successfully unpacked \"${DEB}\" into \"${OUTDIR}\"."