dittoの--bomオプションを使うと、BOM(Bill of Materials)ファイルに記載されたファイルやディレクトリだけを選んでコピーできる。BOMファイルはmkbomで作成し、lsbomで中身を確認する。

参考: 【ditto】コマンドでディレクトリをコピーする

BOMファイルとは

BOMファイルは、ファイルやディレクトリのパス・パーミッション・所有者・サイズ・チェックサムなどを記録したmacOS独自の形式のファイルである。macOSのインストーラーパッケージ(.pkg)は、インストールするファイルの一覧をBOMファイルとして持っている。インストール済みパッケージのBOMファイルは/var/db/receipts/に保存される。

mkbomでBOMファイルを作成する

mkbom <ディレクトリ> <BOMファイル>で、ディレクトリ配下の全ファイルを記録したBOMファイルを作成する。

$ find src -type f | sort
src/bin/tool
src/docs/a.md
src/docs/b.md
src/tmp/cache.log
$ mkbom src full.bom

lsbomでBOMファイルの中身を確認する。

$ lsbom full.bom
.	40755	501/0
./bin	40755	501/0
./bin/tool	100644	501/0	2	2192966820
./docs	40755	501/0
./docs/a.md	100644	501/0	2	2418082923
./docs/b.md	100644	501/0	2	2454254050
./tmp	40755	501/0
./tmp/cache.log	100644	501/0	2	2333201024

-sを付けるとパスだけを表示する。

$ lsbom -s full.bom
.
./bin
./bin/tool
./docs
./docs/a.md
./docs/b.md
./tmp
./tmp/cache.log

一部のファイルだけを記録したBOMファイルを作る

mkbom -iを使うと、lsbomの出力形式のテキストファイルからBOMファイルを作成できる。lsbomの出力から不要な行を取り除けば、特定のファイルを除外したBOMファイルになる。

tmpディレクトリを除外したBOMファイルを作る例は以下の通り。

$ lsbom full.bom | grep -v '^\./tmp' > list.txt
$ mkbom -i list.txt part.bom
$ lsbom -s part.bom
.
./bin
./bin/tool
./docs
./docs/a.md
./docs/b.md

mkbom -iに渡すテキストには、パスだけでなくパーミッションや所有者の列も必要である。lsbom -sの出力をそのまま渡すとmkbom: Not enough fieldsのエラーになる。

また、ファイルの親ディレクトリの行も必要である。親ディレクトリの行を含めずにファイルの行だけを渡すと、BOMファイルに登録されない。

$ lsbom full.bom | grep '^\./docs/a.md' > list3.txt
$ mkbom -i list3.txt p3.bom
parent directory ./docs does not exist
mkbom: Can't insert ./docs/a.md

--bomでBOMファイルに載っているファイルだけをコピーする

ditto --bom <BOMファイル> <コピー元> <コピー先>の形式で実行すると、BOMファイルに記載されたファイルとディレクトリだけをコピーする。

$ ditto --bom part.bom src dst
$ find dst | sort
dst
dst/bin
dst/bin/tool
dst/docs
dst/docs/a.md
dst/docs/b.md

tmpディレクトリはBOMファイルに含まれていないため、コピーされない。

BOMファイルはコピー対象のパスを選ぶためだけに使われる。BOMファイル作成後にファイルの内容が変わっていても、コピー元の現在の内容がそのままコピーされる。BOMファイルに記載されていてもコピー元に存在しないファイルは、エラーを出さずに飛ばされる。

BOMファイルに1つもファイルが含まれていない場合は、警告が表示されて何もコピーされない。

$ ditto --bom p3.bom src dst3
ditto: WARNING: bom p3.bom contains no files, nothing copied

インストール済みパッケージのファイルだけをコピーする

/var/db/receipts/にあるBOMファイルを使うと、特定のパッケージでインストールされたファイルだけを取り出せる。

python.orgのインストーラーで入れたPython 3.11のパッケージのうち、/usr/local/binへコマンドを配置するorg.python.Python.PythonUnixTools-3.11を例にする。BOMファイル内のパスは、パッケージのインストール先(location)からの相対パスになっている。

$ pkgutil --pkg-info org.python.Python.PythonUnixTools-3.11
package-id: org.python.Python.PythonUnixTools-3.11
version: 0
volume: /
location: usr/local/bin
install-time: 1734194695
$ lsbom -s /var/db/receipts/org.python.Python.PythonUnixTools-3.11.bom
.
./2to3
./2to3-3.11
./idle3
./idle3.11
./pydoc3
./pydoc3.11
./python3
./python3-config
./python3-intel64
./python3.11
./python3.11-config
./python3.11-intel64

コピー元にインストール先の/usr/local/binを指定する。

$ ditto --bom /var/db/receipts/org.python.Python.PythonUnixTools-3.11.bom /usr/local/bin pytools
$ ls -1 pytools
2to3
2to3-3.11
idle3
idle3.11
pydoc3
pydoc3.11
python3
python3-config
python3-intel64
python3.11
python3.11-config
python3.11-intel64

/usr/local/binには他のツールのファイルも混在しているが、BOMファイルに記載されたPython関連のファイルだけがコピーされる。

参考

DITTO Command: Copy directory hierarchies, create and extract archives in macOS