フォルダを用意せずに、先に空のdmgを作り、あとからファイルを書き込みたい場合がある。hdiutil createの-sizeで、容量を指定した空のdmgを作れる。作ったdmgをマウントしてファイルをコピーし、配布用には圧縮した形式に変換する。

$ hdiutil create -size 20m -fs APFS -volname "[ボリューム名]" -ov [dmgのファイル名]
$ diskutil image attach --mountOptions nobrowse --mountPoint [マウントポイント] [dmgのファイル名].dmg
$ cp -R [フォルダ]/. [マウントポイント]/
$ diskutil eject [マウントポイント]
$ hdiutil convert [dmgのファイル名].dmg -format UDZO -o [配布用のdmg].dmg

ここでは、空のdmgを作って書き込み、圧縮した形式に変換する手順と、容量が増えるスパースバンドルを紹介する。実行例はmacOS 27.0.1のもの。

参考: 【hdiutil】フォルダからdmgを作る

空のdmgを作る

-sizeに容量を指定して、hdiutil createを実行する。-sizeには、b、k、m、g、tの単位を付けられる。

$ hdiutil create -size 20m -fs APFS -volname Work -ov work

出力先をworkのように拡張子なしで指定すると、work.dmgが作成される。-size 20mで作ったdmgのファイルサイズは、20,971,520バイト(20 MiB)だった。

形式を指定しない場合、dmgは書き込み可能な形式(UDRW)になる。-fsを省略した場合のファイルシステムはAPFSである。

参考: 【hdiutil】dmgのファイルシステムを指定する

作成と同時にマウントする

-attachを付けると、作成したあとにそのままマウントする。マウントポイントを指定しない場合は、/Volumesの下にボリューム名でマウントされる。

$ hdiutil create -size 10m -fs APFS -volname AttachNow -attach -ov an.dmg
/dev/disk22         	GUID_partition_scheme
/dev/disk22s1       	Apple_APFS
/dev/disk23         	EF57347C-0000-11AA-AA11-0030654
/dev/disk23s1       	41504653-0000-11AA-AA11-0030654	/Volumes/AttachNow

マウントしてファイルを書き込む

diskutil image attachで、dmgをマウントする。--readOnlyを付けないため、書き込み可能な状態でマウントされる。

$ diskutil image attach --mountOptions nobrowse --mountPoint [マウントポイント] work.dmg
$ cp -R payload/. [マウントポイント]/
$ ls [マウントポイント]
big.txt
hello
random.bin
README.txt
text.txt

ファイルは、通常のディレクトリと同じようにコピーできる。cp -R [フォルダ]/.の形にすると、隠しファイルも含めてコピーできる。df -hで確認すると、容量20 MBのうち、3.6 MBを使用済みと表示された。書き込みが終わったら、diskutil ejectでマウントを解除する。

$ diskutil eject [マウントポイント]
Disk [マウントポイント] ejected

容量を超えるとエラーになる

dmgの容量より大きいファイルを書き込むと、エラーになる。20 MBのdmgに、30 MBのファイルをコピーした例を示す。

$ cp big30.bin [マウントポイント]/
cp: error writing '[マウントポイント]/big30.bin': No space left on device
$ echo $?
1

使用中のdmgはイジェクトできない

dmgの中をカレントディレクトリにしているシェルがあると、diskutil ejectは失敗する。

$ cd [マウントポイント]
$ diskutil eject [マウントポイント]
Unmount was dissented by PID [PID] (/bin/zsh)
Dissenter parent PPID [PPID] (/bin/zsh)

メッセージに、マウントポイントを使っているプロセスのPIDが表示される。シェルのカレントディレクトリをdmgの外に移動してから、もう一度diskutil ejectを実行すると、マウントを解除できた。

配布用の圧縮した形式に変換する

書き込み可能な形式のdmgは、容量の分だけファイルが大きく、配布には向かない。hdiutil convertで、圧縮した読み取り専用の形式に変換する。

$ hdiutil convert work.dmg -format UDZO -o final.dmg
経過時間:  3.285s
ファイルサイズ: 3020833バイト、チェックサム: CRC32 $DC600151
処理されたセクタ数: 40960、7507圧縮されました
速度: 1.1Mバイト/秒
節約率: 85.6%
created: [final.dmgのフルパス]
hdiutil: WARNING: 'hdiutil convert -format ...' is deprecated. Please use 'diskutil image create from --format ...' instead.

20 MiBのwork.dmgが、約3 MBのfinal.dmgになった。出力先に同名のdmgがある場合、hdiutil convertはファイルが存在しますのエラーになる。変換したdmgをマウントして、中身が元のファイルと同じであることを確認した。

$ diskutil image attach --mountOptions nobrowse --readOnly --mountPoint [マウントポイント] final.dmg
$ cmp [マウントポイント]/random.bin payload/random.bin && echo identical
identical

diskutilで変換する

hdiutil convertは非推奨で、警告が、diskutil image create from --formatへの移行を勧めている。diskutil image create fromは、既存のdmgを元に、別の形式のdmgを作れる。

$ diskutil image create from --format UDZO work.dmg final2.dmg
final2.dmg created

final2.dmgは、final.dmgとほぼ同じ約3 MBのUDZO形式のdmgになった。

参考: 【diskutil】image createでdmgを作る

参考: 【hdiutil】dmgの圧縮形式を選ぶ

容量が増えるスパースバンドルを作る

-type SPARSEBUNDLEを指定すると、中身に合わせて、ディスク上の容量が増えるdmgを作れる。-sizeには、ボリュームの最大容量を指定する。

$ hdiutil create -type SPARSEBUNDLE -size 1g -fs APFS -volname Grow -ov grow
$ ls -d grow*
grow.sparsebundle

スパースバンドルは、ファイルではなく、.sparsebundleというディレクトリである。diskutil image attachで、通常のdmgと同じようにマウントできる。

ボリュームの容量は1 GBだが、作成した直後のディレクトリのサイズは、約19.7 MBだった。100 MBのファイルを書き込むと約114 MBになり、ファイルを削除してマウントを解除すると、約24 MBに減った。

$ du -sk grow.sparsebundle
19748	grow.sparsebundle

compactで空き領域を解放する

hdiutil compactで、スパースバンドルの未使用の領域を解放できる。

$ hdiutil compact grow.sparsebundle
圧縮を開始中…
空き領域を再び利用可能にしています…
圧縮の仕上げ中…
空き領域1020.7 MBのうち4.8 MBを再び利用可能にしました。

compactの前後で、ディレクトリのサイズは、約24 MBから約19 MBになった。続けて2回目を実行すると、解放できる領域がなく、0 bytesと表示された。

配布するdmgは、スパースバンドルではなく、圧縮した形式のdmgにする。man hdiutilには、スパースバンドルをUDZOに変換する例が載っている。