pkgutilの便利なコマンドをまとめてみた



# はじめに

Macにアプリをインストールする場合、一般的な方法として以下の２つのインストール方法がある。

1. `/Applications`フォルダに直接dmg形式のファイルコピーする
2. installer経由でpkg形式のファイルをインストールする

今回は後者のinstallerを使ったpkg形式のアプリのインストールする際に役立つコマンドラインツール`pkgutil`について解説していく。

# pkgutilのコマンド
pkgutilはMacOSにデフォルトで入っているコマンドラインツールだ。
manページの説明は以下の通り
> pkgutil -- Query and manipulate MacOS X Installer packages and receipts


インストーラの`パッケージ(.pkgファイル)`とインストール後に記録される`レシート`を操作・照会するためのツールだということが分かる。
パッケージをインストールする前に照会することで、どのようなパッケージであるか、どのようなファイルをインストールするのか確認できる。
レシートを操作するとMacOS上にインストールしたパッケージの情報やそのパッケージがインストールしたファイルの情報などを取得できる。

まずはパッケージをインストールする前に使えるコマンドを紹介する。

## パッケージをインストールする前に使えるコマンド

パッケージをインストールする前に、私がよく使う３つのオプションを紹介する。

### パッケージの署名情報を確認する

以下のコマンドでパッケージの署名情報を確認できる。
```
> pkgutil --check-signature <パッケージのパス>
```
署名されていない場合 `no signature`が返され、署名されている場合はその署名情報が返ってくる。
試しにMicrosoft Teamsのインストーラパッケージで確認すると以下の結果が返ってくる。
```
Package "Teams_osx.pkg":
Status: signed by a developer certificate issued by Apple for distribution
Signed with a trusted timestamp on: 2021-12-21 23:05:05 +0000
...
``` 

### パッケージ内のインストールファイルを確認する

以下のコマンドでパッケージに含まれるインストールされるファイル(ペイロードファイル)を確認できる。
```
> pkgutil --payload-files <パッケージのパス>
```
試しにMicrosoft Teamsのインストーラパッケージで確認すると以下の結果が返ってくる。
```
> pkgutil --payload-files Teams_osx.pkg 
```

```
.
./Microsoft Teams.app
./Microsoft Teams.app/Contents
./Microsoft Teams.app/Contents/_CodeSignature
./Microsoft Teams.app/Contents/_CodeSignature/CodeResources
./Microsoft Teams.app/Contents/MacOS
./Microsoft Teams.app/Contents/MacOS/Teams
…
```

出所が不明なフリーソフトなどのインストーラはこのコマンドにて事前に確認することで、マルウェアなどの怪しいファイルがインストールされることがないか確認できる。

### パッケージの解凍

.pkgファイルは一種の圧縮ファイルであり、pkgutilコマンドで解凍することができる。

パッケージを解凍する理由としては、主に以下の2つがある。
1. インストールの構成やインストール時に実行されるスクリプトを確認する
2. 実際にインストールされるファイル(ペイロードファイル)の内容を確認する

以下のコマンドを実行すると任意のフォルダにパッケージが解凍される。
```
> pkgutil --expand <パッケージのパス> <解凍後のフォルダのパス>
```
または
```
> pkgutil --expand-full <パッケージのパス> <解凍後のフォルダのパス>
```

manページには`--expand`オプションの情報しか記載されていないが`--expand-full`オプションが存在している。
この2つのオプションは、スクリプトや構成情報などのファイルはどちらも解凍されるが、配布パッケージ（distributionパッケージ）に含まれるペイロードファイルの取り扱いが異なる。

`--expand`では`Payload`という*ファイル*が作成され、1つのバイナリファイルアーカイブされた状態になるため、インストールされるファイルの内容を確認できない。
`--expand-full`を使うと`Payload`という*フォルダ*が作成され、インストールされるファイルの内容をひとつひとつ確認できる。

つまり、インストール前にペイロードファイルの内容を確認したい時は`--expand-full`を使う必要がある。
構成情報やスクリプトを参照したいだけであれば`--expand`を使えば良い。
私のおすすめはいずれの場合でも対応ができる`--expand-full`を使うことだ。

スクリプトについて補足するが、Macのパッケージインストーラはペイロードファイルのインストール前後に実行されるスクリプトをパッケージ作成時に指定できる。
このスクリプトもパッケージを解凍することで確認できる。
パッケージを解凍すると`Script`というフォルダが作成され、そのフォルダ内の`preinstall`と`postinstall`というファイルがそれぞれインストール前、インストール後に実行されるスクリプトだ。

先ほど例に使っていたTeamsのパッケージの`preinstall`スクリプトはシェルスクリプトなのでテキストエディタなどで内容が確認できる。

```sh
#!/usr/bin/env sh

LOGDIRECTORY="/Library/Logs/Microsoft/Teams"
LOGFILE=$LOGDIRECTORY/updater.log
KILLED_TEAMS_FILE=$LOGDIRECTORY/KilledTeams.txt

# ensure we have a log directory before logging
/bin/mkdir -p $LOGDIRECTORY
...
```

このようにファイルを配置するだけではなく、インストーラはスクリプトで様々なことができるので興味があったら是非インストーラの中身を覗いてみよう。

なお、配布パッケージと単体パッケージの違いについてこの記事では触れないが、配布パッケージは`productbuild`で作成するパッケージのことだ。
[この記事](https://qiita.com/ioue/items/9d1681286c50f8e7e98d)などでパッケージ作成方法を読むと分かりやすいかもしれない。


次にパッケージのインストール後に使えるコマンドを紹介する。

## パッケージのインストール後に使えるコマンド
MacOSにパッケージをインストールするとレシートという形でパッケージの情報が残る。
pkgutilを使うとそのレシートに書かれている情報を確認できる。

### インストールしたパッケージの列挙
以下のコマンドで指定のボリュームにインストールしているパッケージを列挙できる。
```
> pkgutil --pkgs --volume <ボリュームのパス>
```
ボリュームを指定しない場合`/`になる。

このコマンドを使うと以下のようにインストールしているパッケージのID一覧が返ってくる。

```
com.apple.pkg.MobileDevice
com.apple.pkg.MobileDeviceDevelopment
com.microsoft.teams
com.apple.pkg.Xcode
…
```
パッケージIDはリバースDNS形式で書くのがAppleから推奨されているのでその形式が一般的だ。
弊社でであれば`jp.co.sciencepark.examplePkgID`というパッケージIDになる。

### パッケージの情報を確認する

インストールされているパッケージの詳細情報を確認したい場合には、以下のコマンドでパッケージIDに対応する詳細情報が取得できる。

```
> pkgutil --pkg-info=<パッケージID> --volume <ボリュームのパス>
```

バージョン、インストールされているパス、インストール日時（Epochからの秒数）が取得できる。
インストール日時は以下のコマンドで日付表示に変更できる。
```
> date -r <秒数>
```


試しにMicrosoft Teamsのレシートで確認すると以下の結果が返ってくる。
```
package-id: com.microsoft.teams
version: 1.00.XXXXXX
volume: /
location: Applications
install-time: XXXXXXXXXX
```


### パッケージがインストールしたファイルを確認する

インストール前のコマンドで紹介した`payload-files`のインストール後版のコマンドだ。
```
> pkgutil --files=<パッケージID> --volume <ボリュームのパス>
```

パッケージがインストールしたファイル一覧が取得できる。

インストールしたアプリにアンインストーラが含まれていなかった場合、このコマンドで取得した情報を使ってインストールされたファイルをアンインストールできる。



アンインストール方法は[この記事](https://qiita.com/ysk24ok/items/2202b483cd068ec824eb)で紹介されている。
簡単に説明すると`pkgutil --files`でパッケージがインストールしたファイルを列挙して、それを１つずつ削除していく。
最後に`pkgutil --forget`を使ってパッケージのレシート情報を削除する。

記事にも注意書きとして書いてあるように、アプリケーションがインストール後に作ったユーザー設定ファイルなどの情報は載っていないのでそのようなファイルはこの方法を使って削除できないので要注意だ。

# 終わりに
以上がパッケージを操作するにあたって私がよく使うpkgutilのコマンドだ。

皆さんの役に立てばとまとめてみた。