macOS の open コマンドで開くアプリのパスを取得する（alias に紐づいたパスの取得）

## `open` コマンドで開くアプリケーション本体のパスが知りたい

macOS で、コマンドライン（ターミナル）やシェルスクリプトなどから GUI アプリを起動する場合は、以下のように `open` コマンドに `-a` オプションを指定します。

> `$ open -a "VLC"`

上記の場合、通常は `/Applications/VLC.app` にあると想定されるのですが、容量確保のために別ドライブに移動されていたり、特定ユーザー用にユーザのアプリフォルダ（`/User/hoge/Applications/VLC.app`）に移動している可能性があります。それでも、上記コマンドで起動します。

そのため、スクリプトから「████.app」内のリソースにアクセスしたくても、**アプリが必ず「`/Applications`」にあるとは限らない**ので困ったのです。

また、`find` コマンドで検索しようにも**複数バージョンがある場合、どれが OS に紐づいているかわからない**のです。

## TL;DR

以下の２通りの方法があります。

> 1. `AppleScript` でシステムに紐づいたパスを取得する
> 2. `bash` で [`lsregister` コマンド](https://ss64.com/osx/lsregister.html)のダンプから `grep` して取得する

### AppleScript POSIX版（正確）

- メリット　：アプリの起動を確かめてから返すので正確
- デメリット：アプリが起動してしまう

```shell-session:Applicationフォルダにあるアプリ
$ osascript -e 'POSIX path of (path to application "Google Chrome")'
/Applications/Google Chrome.app/
```
```shell-session:別ドライブにインストールされているアプリ
$ osascript -e 'POSIX path of (path to application "GIMP")'
/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app/
```

CLI で GIMP の本体バイナリからバージョンを表示させるシェル・スクリプトの例

```bash:findVerGIMP.sh
#!/bin/bash

PATH_GIMP=$(osascript -e 'POSIX path of (path to application "GIMP")')

echo $($PATH_GIMP/Contents/MacOS/GIMP-bin --version)
```
```shell-session:上記スクリプトの結果
$ ./findVerGIMP.sh
GIMP (GNU Image Manipulation Program) ver.2.8.18
```

### lsregister コマンド版

AppleScript の `path to application` を使うと**アプリが起動してしまうのが嫌**な人向け。

- メリット　：`bash`である
- デメリット：単体コマンドとしては使いづらい。`lsregister`の DB を元にしているので正確性に欠ける

macOS の `lsregister` コマンドでダンプ（登録アプリの情報）を取得し、`grep` で取得する。

#### 汎用

```bash:findApp
#!/bin/bash

# findApp.sh
# ==========
NAME_APP=$1
PATH_LAUNCHSERVICES="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"
${PATH_LAUNCHSERVICES} -dump | grep -o "/.*${NAME_APP}.app" | grep -v -E "Caches|TimeMachine|Temporary|/Volumes/${NAME_APP}" | uniq
```

```shell-session:使い方
$ ./findApp GIMP
/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app
```

#### 応用

```shell-session:Applicationフォルダにあるアプリ
$ cd /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/
$ ./lsregister -dump | grep -o "/.*\Google Chrome.app" | head -1
/Applications/Google Chrome.app
```
```shell-session:別ドライブにインストールされているアプリ
$ cd /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/
$ ./lsregister -dump | grep -o "/.*GIMP.app" | head -1
/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app/
```

CLI で GIMP の本体バイナリからバージョンを表示させるシェル・スクリプトの例

```bash:findVerGIMP.sh
#!/bin/bash

launchServicesPath="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump"

PATH_GIMP=$(${launchServicesPath} | grep --only-matching "/.*GIMP.app" | head -1)

echo $($PATH_GIMP/Contents/MacOS/GIMP-bin --version)
```
```shell-session:上記スクリプトの結果
$ ./findVerGIMP.sh
GIMP (GNU Image Manipulation Program) ver.2.8.18
```

### 検証済み環境

- macOS HighSierra (OSX 10.13.6)
- `bash --version`: GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)

---

## TS;DR（結論にたどり着くまでの過程）

### Mac の ".app" はアプリじゃないの？林檎なの？

Mac から VLC や GIMP といった動画や画像系**アプリをコマンドラインで操作したかった**のです。

[VLC](https://ja.wikipedia.org/wiki/VLC%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%83%97%E3%83%AC%E3%83%BC%E3%83%A4%E3%83%BC) には、コマンドラインから操作が行える [CVLC](https://wiki.videolan.org/Documentation:Command_line/#Use_the_command_line) と呼ばれるコマンドーな機能があるらしく、複数の動画に同じ操作を行うのに便利そうです。「詳しくは `$ vlc --longhelp` を見よ」とあります。

[GIMP](https://ja.wikipedia.org/wiki/GIMP) にも、コマンドラインから画像処理を行える[バッチモード](https://www.gimp.org/tutorials/Basic_Batch/)と呼ばれるバッチコイな機能が付いているらしく、複数の画像に同じ加工処理をカマすのに便利そうです。「詳しくは `$ gimp --help` を見よ」とあります。


```shell-session:ターミナル操作
$ vlc --longhelp
-bash: vlc: command not found
$ 
$ gimp --help
-bash: gimp: command not found
```

「そんなものはない 👋Bash!」と ﾊﾞｼｯ っと言われてしまいました。確かに [`which`](https://www.google.co.jp/search?q=site%3Aqiita.com+mac+which+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)コマンドで確認（`$ which VLC`） しても表示されません。

「あれ？ あ、そうか そうか アプリケーションフォルダに行かないといけないのね」と思ったのもつかの間、**あることを根本的に間違っている**ことに気付きました。

```shell-session:ターミナル操作
$ cd /Applications
$ ls | grep VLC
VLC.app
$ 
$ ./VLC --longhelp
-bash: ./vlc: No such file or directory
$ 
$ ./VLC.app --longhelp
-bash: ./vlc.app: is a directory
```

そうでした。Mac の「`/Applications`」フォルダにあるアプリはアプリでも**「.app」はバイナリ・ファイルではない**のでした。「アプリケーション・バンドル」と呼ばれる、ディレクトリなのでした。こりゃまた失念。

そこで、[ブラウザをシークレットモード（プライベートモード）で起動するときに引数でオプションを渡す](https://www.google.co.jp/search?q=site%3Aqiita.com+macOS+%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6+%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3+%7B%E3%83%97%E3%83%A9%E3%82%A4%E3%83%99%E3%83%BC%E3%83%88+%E3%82%B7%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88+incognito%7D)のと同じようにオプションでコマンド引数を渡してみました。

```shell-session:ターミナル操作
$ open -a "VLC" --args --longhelp
$
```

反応ナッシングです。起動すらしません。

では、**「`.app`」の本体バイナリを叩いたらどうか**と、ディレクトリ内にあるファイルを探してみました。

### macOS アプリ（`〜.app`）の本体バイナリのパス

複数アプリ（`〜.app`）のディレクトリ内を調べてみたところ、どうやら macOS の場合、「████.app」の**バイナリ本体は「████.app/Contents/MacOS」のディレクトリ下にある**らしく、直接バイナリを叩いてみたところヘルプが表示されました。

```shell-session:ターミナル操作
$ # アプリケーション・ディレクトリに移動
$ cd /Applications
$ ls -la | grep VLC
drwxr-xr-x   3 admin  admin     96  6  2 19:24 VLC.app
$
$ # .app のバイナリ本体ディレクトリに移動
$ cd VLC.app/Contents/MacOS
$ ls -la
total 80
drwxr-xr-x    7 admin  admin    224  5 30 15:39 .
drwxr-xr-x    8 admin  admin    256  5 30 15:39 ..
-rwxr-xr-x    1 admin  admin  38880  5 30 15:39 VLC      ←いた
drwxr-xr-x    3 admin  admin     96  5 30 15:18 include
drwxr-xr-x    6 admin  admin    192  5 30 15:37 lib
drwxr-xr-x  342 admin  admin  10944  5 30 15:37 plugins
drwxr-xr-x    5 admin  admin    160  5 30 15:18 share
$
$ ./VLC --longhelp
VLC media player 3.0.3 Vetinari (revision 3.0.3-1-0-gc2bb759264)
利用方法: vlc [オプション] [ストリーム] ...
コマンドライン上で複数のストリームを指定することが可能です。
指定されたストリームはプレイリストにキューイングされます。
最初に指定されたものから順に再生されます。

オプションの指定形式:
  --option  プログラムの長さを指定するグローバルオプション
   -option  グローバルオプション --option の一文字バージョン
   :option  ストリームに直接適用するオプション、前の設定は上書きされます。
（以下略）
```

:thumbsup: 

「GIMP」の方は、**空き容量を確保するためインストール後、別ドライブに移動した**のですが、同じように「GIMP.app/Contents/MacOS」ディレクトリにバイナリがあり、コマンド引数を受け取ってくれました。

```shell-session:ターミナル操作
$ # 別ドライブのインストール先に移動
$ cd /Volumes/External_HDD/Applications/GIMP/GIMP_v2.8
$ ls -la
drwxr-xr-x@  3 admin  staff    102 11  6  2016 GIMP.app
$
$ # .app のバイナリ本体ディレクトリに移動
$ cd GIMP.app/Contents/MacOS
$ ls -la
total 16112
drwxr-xr-x@ 5 admin  staff      170 11  6  2016 .
drwxr-xr-x@ 6 admin  staff      204 11  6  2016 ..
-rwxr-xr-x@ 1 admin  staff     2920 12 14  2015 GIMP
-rwxr-xr-x@ 1 admin  staff  8226028 11  6  2016 GIMP-bin ← いた
-rwxr-xr-x@ 1 admin  staff    13088 11  6  2016 python
$
$ ./GIMP-bin --help
Usage:
  GIMP-bin [OPTION...] [ファイル|URI...]

GIMP (GNU Image Manipulation Program)

Help Options:
  -h, --help                          Show help options
  --help-all                          Show all help options
  --help-gegl                         Show GEGL Options
  --help-gtk                          GTK+ のオプションを表示する
（以下略）
```

:thumbsup: 

これでバッチリンコです。自動化作業もバッチコーイとお尻ペンペンできそうです。

ある１点を除けば。

### いつからアプリが「`/Applications`」にあると錯覚していた？

上記の VLC のように、該当アプリが「`/Applications`」や「`~/Applications`」ディレクトリに必ずあるとは限りません。GIMP のように空き容量が足りないので別ドライブに入れている人もいるはずです。

これでは、自動化スクリプトを作ろうにも、別ドライブに移動したアプリの場合は、**パスをハードコーディングする（スクリプトに埋め込む）必要があり、汎用性に欠ける**問題があります。

そこで、困った時の [StackOverflow で聞いてみた](https://stackoverflow.com/questions/51296900/on-macos-how-can-i-find-the-apps-path-which-opens-with-open-command)のですが、

> 「`open`コマンド経由で オプションを渡しても無理。起動プロセスが異なるため STDIN, STDERR （標準入力や標準エラー）といった情報を受け取れないから。ゴリゴリ検索するしかないんじゃね？」（筆者訳）

という回答が。

確かにスクリプト内で [`find` コマンド](https://www.google.co.jp/search?q=site%3Aqiita.com+find+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89+%E4%BD%BF%E3%81%84%E6%96%B9+mac)を使って `/Volumes` ディレクトリ内を検索しようとも思ったのですが、別ドライブには複数バージョンが入っており、どれが `open` コマンドで開けるアプリ（OS に紐づいたアプリ）なのか分かりません。

`$ open -a "/Applications/VLC.app"` とフルパスでなくても `$ open -a "VLC"` で開く、つまり `alias` を張ってるということは、どこかに関連付けの情報があるはず。

すると [StackExchange](https://superuser.com/questions/570112/how-do-i-find-the-path-to-a-program-in-terminal#570120) で、以下のような知見が得られました。

> If you're looking for a simple way to determine where an OS X (GUI) application bundle is installed (as used e.g. by the `open` command), you can execute the following short AppleScript from the command line:
>
> 訳：OS X の GUI アプリ（`.app`）がどこにインストールされているか、簡単な方法をお探しでござれば、以下の短い AppleScript をコマンドウラインから実行されてみてはいかがでござろうか：
>
> ```shell-session:ターミナル
> $ open -a "Safari"
> $ osascript -e 'tell application "System Events" to POSIX path of (file of process "Safari" as alias)'
> /Applications/Safari.app
> ```

某ネットの **osa**murai さんのアドバイスにしたがってローカルの環境で **osa**script を試したところ、パスが表示されました！まさに、これです。

```shell-session:ターミナル
$ open -a "VLC"
$ osascript -e 'tell application "System Events" to POSIX path of (file of process "VLC" as alias)'
/Applications/VLC.app
```

ただ、事前にアプリを起動しておかないとエラーで取得できませんでした。

```shell-session:ターミナル
$ osascript -e 'tell application "System Events" to POSIX path of (file of process "VLC" as alias)'
79:84: execution error: file of «class prcs» "VLC" of application "System Events"のタイプをaliasに変換できません。 (-1700)
```

これではいささか不便です。

`kill` コマンドで終了させるのも何かが違う。これは `osascript` なるコマンドの文法を勉強しないといけないようです。

### `osascript` 概要

「`osascript`」。聞いたことがあるようで、よくわからないのですが、なんか強そうです。文脈からするに AppleScript を実行するのに使われるコマンドのようです。

[Wikipedia](https://ja.wikipedia.org/wiki/AppleScript) によると、「[OSA](https://ja.wikipedia.org/wiki/Open_Scripting_Architecture) に準拠したスクリプトを叩くコマンド」のようで、AppleScript や Javascript などが実行できるとのこと。

「`APP_NAME.app`」ディレクトリをアプリとみなす macOS 独自の仕様であれば AppleScript を使って環境変数を取得するというのも何かガテンが行きました。

`AppleScript` には馴染みがないのですが、先の例文や[ファイル参照の例文](http://d.hatena.ne.jp/zariganitosh/20100924/apple_script_alias_posix_file)を見ると、英語の自然言語に近い文法らしく、`tell application to 〜` のように `（動詞 名詞） to （対象）` の組み合わせといった「DO WHAT」形式を基本とした構文のようです。

また、`$ osascript -e` の `-e` オプションについて `$ man osascript` でマニュアルを見ると、コマンドラインから命令を実行する場合に使えるようです。

### `Applescript` の構文解析

~~深く考える前に手が出るタイプなので~~下調べに飽きたので、まずは以下のコマンドを打ってみました。

```shell-session:"path␣of"を使ってみる
$ osascript -e 'path of application "GIMP"'
0:4: execution error: GIMPでエラーが起きました: pathを取り出すことはできません。 (-1728)
$ 
$ osascript -e 'POSIX path of application "GIMP"'
0:10: execution error: GIMPでエラーが起きました: POSIX pathを取り出すことはできません。 (-1728)
```

なんか怒られてしまいました。

"`execution error`" とあるので、どうやら `hoge of fuga` の構文は「`fuga` に `hoge` を実行した結果を返す」動きをするようです。つまり「`hoge` は `fuga` のメソッドやプロパティにはない」と言ったエラーであるように見受けられました。

次に、`tell application to` の構文を参考に `path of` から `path to` に変えてみました。

```shell-session:"path␣to"を使ってみる
$ osascript -e 'path to application "GIMP"'
alias External_HDD:Applications:GIMP:GIMP_v2.8:GIMP.app:
```

おお。なんかそれっぽいものが出てきましたよ。

どうやら、`hoge to fuga` の構文の場合は「`fuga` の `hoge`」を取り出す、つまり `path to application "GIMP"` は「`application "GIMP"` の `path`」を取り出してくれるようです。

また、取り出した値は「`alias`」で、リンク先は「`External_HDD:Applications:GIMP:GIMP_v2.8:GIMP.app:`」にある、と。実際のパスも「`/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app/`」にあるので、いい感じに近付いて来ました。

[調べてみると](http://tonbi.jp/AppleScript/Introduction/11/)、macOS の場合、パスの表記のディレクトリ・セパレーターには大きく２通りの表記方法があることがわかりました。

> 1. `UNIX系のOSで使われる「 / 」区切りのパスの事を特に「POSIXパス」と言います。`
> 2. `HFSではファイル名を「 : 」(コロン)で区切って使います。`

先に述べた [AppleScript のファイル参照例一覧](http://d.hatena.ne.jp/zariganitosh/20100924/apple_script_alias_posix_file)にも「`Mac形式のパステキストは、ディレクトリを":"で区切る`」とあります。Windows で言う「`\`（&yen;）」と似た感じですね。

`POSIX` は先ほどからチラチラ出てきています。

> `$ osascript -e 'tell application "System Events" to POSIX path of (file of process "Safari" as alias)'`

コマンドの実行部分（`-e`の引数）を抜き出すと、以下のようになります。

> `tell application "System Events" to POSIX path of (file of process "Safari" as alias)`

上記は長ったらしく咀嚼できないので、噛み砕く必要があります。

「英語←→日本語」に限らず、**解釈が難しいときは分解して逆から読んでいくと把握しやすくなる**ことが多くあります。

そこで、まずは以下のように分解してみます。

````bash
$a = 「tell application "System Events" {$b}」
$b = 「POSIX path of {$c}」
$c = 「file of process {$d}」
$d = 「"Safari" as alias」
````

次に基本命令より後をフリップして日本語に置き換えて行きます。

```bash
$a = 「tell application "System Events" {$b}」（１）
$d = 「"Safari" as alias」（２）
$c = 「file of process {$d}」（３）
$b = 「POSIX path of {$c}」（４）
```

- （１）「アプリ "System Events" に以下を返すように指示」
- （２）「文字列 "Safari" をエイリアスとした」
- （３）「プロセスのファイル名を返せ」（先の "a of b" の動きより）
- （４）「いや、返ってきた値の POSIX path を返せ 」（同上）

ここで必要なのは（４）「`POSIX path of X`」の POSIX パスに変換する部分です。

以上を組み合わせたところ、ちゃんと一般的な（`bash` などで使える）パスで取得できました。

```shell-session
$ # アプリケーションのパスを Mac 形式で取得
$ osascript -e 'path to application "GIMP"'
alias External_HDD:Applications:GIMP:GIMP_v2.8:GIMP.app:
$
$ # Mac 形式のパスを POSIX 形式に変換
$ osascript -e 'POSIX path of (path to application "GIMP")'
/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app/
```

以上で ~~面倒になったので~~ **必要な情報の取得の仕方はわかった**ので、これ以上 AppleScript と `osascript` コマンドについて勉強するのを止めました。

今回の最低限必要な情報は得られたし、また必要に迫られた時に、必要な部分にターゲットを絞って調べたいと思います。（パラシュート学習法）

問題は、**たかがパスを取得するだけのために、ググってもドンピシャの情報が無い**ので、ここまで基本を調べないといけなかったこと、三歩歩けば忘れるどころか、**どこを歩いたのかすら忘れる人間**なので、Qiita にパンくずを残しておきたいと思って記事にしました。

未来の俺よ [DRY](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself) で乾杯と行こうぜ！

### 過去の俺よ、今日は雨だ

2018/08/11、雨のち台風。過去の俺、元気にしてますか？

DRY どころか WET な日に、気付いてしまいました。上記の **AppleScript で `path to application` を使うとアプリが毎回起動するのがウザ**いことに。。。

GUI アプリであるため `path to application` で呼び出されるとアプリが起動してしまうのは仕方ないと仕様割り切りとしていました。

しかし、とあるツールを作っていた時に「こりゃ、**毎回アプリが起動するのは用途によっては不便だな**」と感じました。

というのも、そのツールは macOS にインストール済みのブラウザを取得するのに上記スクリプトを利用していたのです。

①「アプリのパスが戻って来ればインストール済みと判断」し、（インストール済みの）②「ブラウザ一覧から任意のブラウザを選択できる」ようにしたツールなのですが、**アプリのパスを取得する度に全ての種類のブラウザが起動してしまう**のですよ。

やはり困ったときの StackOverflow です。同じ悩みの投稿を見つけました。

- [Applescript: Get path to .app without opening it](https://stackoverflow.com/questions/26636860/applescript-get-path-to-app-without-opening-it/26639490) @ StackOverflow

[ベスト回答](https://stackoverflow.com/a/26639490/8367711)を見ると、**Applescript の `path to` メソッドは [`Standard Addition`](https://en.wikipedia.org/wiki/Standard_addition) つまり「『標準添加法』のメソッドのようなもの」でアプリを起動させないと得られない**らしいのですが、、、よく[意味がわかりません](http://www.horiba.com/jp/application/material-property-characterization/water-analysis/water-quality-electrochemistry-instrumentation/ph-knowhow/qa-ion/measurement/6/)。

どうやら、[英語の Wikipedia](https://en.wikipedia.org/wiki/Standard_addition) を読むと「対象に、分かっているものを添加することで反応を得る」メソッド（手法）のようです。よくわからない生き物にリンゴを与えたらゴリラと分かった、みたいなものでしょうか。

つまり、先述の「Applescriptの構文」にあった「`hoge to fuga` は `fuga の hoge` を取り出す意味である」と仮定しましたが、厳密には「`hoge を fuga に渡した場合の反応`」ということのようです。

`path to application "GIMP"` は「"GIMP" Application に "path" を渡した場合の戻り値」ということになります。なるほど、惜しいですが、しっくりきました。

また、ベスト回答には「`Launch Services Registry` の内容を `grep` してはどうか」と以下のよう提案していました。

```applescript:sample.oscp
getAppPath("TextEdit.app")

on getAppPath(appName)
    try
        set launchServicesPath to "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister"

        -- get the path to all executables in the Launch Service Registry that contain that appName
        set appPaths to paragraphs of (do shell script launchServicesPath & " -dump | grep --only-matching \"/.*\\" & appName & "\"")
        return appPaths

    on error
        return "NOT INSTALLED"
    end try
end getAppPath
```
- [Applescript: Get path to .app without opening it
](https://stackoverflow.com/a/26639490/8367711) @ StackOverflow

「あぁ、AppleScript の関数にせなあかんのか」と思ったのですが、よくみると `do shell script` とあり Applescript からシェルを叩いているようです。

```applescript
(do shell script launchServicesPath & " -dump | grep --only-matching \"/.*\\" & appName & "\"")
```

`launchServicesPath` や `appName` は、上の箇所ので `set appName` `set launchServicesPath` とあるので変数であることがわかります。

つまり、シェルで「絶対パスでコマンドを叩いて、`grep` でアプリ名を抜き出しているだけ」ということがわかりました。また、そのコマンドには `-dump` オプションを付けていることも。

次に、変数 `launchServicesPath` に代入されたコマンドの絶対パスを見ると、コマンド名は `lsregister` であることがわかります。

> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister

調べる前に手が出てしまう悪い癖がここでも起きるのですが、引数（オプション）なしで叩いてみると以下のメッセージが。

```shell-session
$ cd /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/
$ 
$ ./lsregister
lsregister: [OPTIONS] [ <path>... ]
                      [ -apps <domain>[,domain]... ]
                      [ -libs <domain>[,domain]... ]
                      [ -all  <domain>[,domain]... ]

Paths are searched for applications to register with the Launch Service database.
Valid domains are "system", "local", "network" and "user". Domains can also
be specified using only the first letter.

  -kill     Reset the Launch Services database before doing anything else
  -seed     If database isn't seeded, scan default locations for applications and libraries to register
  -lint     Print information about plist errors while registering bundles
  -lazy n   Sleep for n seconds before registering/scanning
  -r        Recursive directory scan, do not recurse into packages or invisible directories
  -R        Recursive directory scan, descending into packages and invisible directories
  -f        force-update registration even if mod date is unchanged
  -u        unregister instead of register
  -v        Display progress information
  -dump     Display full database contents after registration
  -h        Display this help
```

`-dump` オプションもちゃんといます。

どうやら、この **`lsregister` コマンドとは "Launch Service" の DB にアプリケーションを登録して紐づけするためのコマンド**のようです。そして `-dump` オプションはその DB の中身を吐き出すもののようです。

それでは、と、また後先考えずに「`$ ./lsregister -dump`」と叩いたところ、出るわ出るわ、ずらずらと色々な情報が流れて来ます。

なるほど、特定アプリの情報を返すオプションもないため、確かに `grep` で絞り込まないといけないなと思いました。では、単純に "GIMP" で絞り込んでみます。

```shell-session:bash
$ ./lsregister -dump | grep "GIMP"
	path:          /Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app
	name:          GIMP
	executable:    Contents/MacOS/GIMP
    CFBundleExecutable = GIMP;
    CFBundleGetInfoString = "2.8.18, \U00a9 1995-2016 The GIMP Development Team";
    CFBundleName = GIMP;
    NSHumanReadableCopyright = "\U00a9 1995-2016 The GIMP Development Team";
	path:          /Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app
	name:          GIMP
	executable:    Contents/MacOS/GIMP
    CFBundleExecutable = GIMP;
    CFBundleGetInfoString = "2.8.18, \U00a9 1995-2016 The GIMP Development Team";
    CFBundleName = GIMP;
    NSHumanReadableCopyright = "\U00a9 1995-2016 The GIMP Development Team";
```

おぉ、いい感じです。しかし、同じ内容のものが２セットあります。ここで初めて [`lsregister` なるコマンドをググってみました](https://www.google.co.jp/search?q=lsregister)。

[Qiita の記事](https://qiita.com/1000ch/items/e1a67157272ab5887563)に次いで [英文のマニュアルのページ](https://ss64.com/osx/lsregister.html)が表示されました。さすがは Qiita。

- [Macのコンテキストメニューをリフレッシュする](https://qiita.com/1000ch/items/e1a67157272ab5887563) @ Qiita
- [lsregister Launch Services Man Page](https://ss64.com/osx/lsregister.html) | macOS @ SS84.com

どうやら、コンテキストメニュー（右クリックで表示されるメニュー）に表示される「このアプリケーションで開く」に表示されるアプリの一覧など、**アプリを起動するためのサービスを `Launch Services` と呼んでいる**ようです。

そして、それらのアプリ一覧は内部の DB に保存されているとのこと。この **`lsregister` コマンドはそれらを登録したり、リフレッシュしたりするためにある**ようです。

今後、**調べる時は「`Launch Service`」をキーワードにした方が良さそう**です。

また、これらに登録されるデータは、「〜.app」内にある「〜.plist」などを元に引っ張ってくるとのこと。たまに、複数の同じアプリやアンインストールしたはずのアプリがコンテキストメニューに表示されることがあるのは、この DB に重複して登録されたり、登録削除の漏れがあったからなんですね。

なるほど。となると、先ほど複数の "GIMP" が表示されたのは、そういうことなので、とりあえずは `grep` で最初に表示されたアプリで絞れば良さそうです。（本当は `grep` 後、ディレクトリの確認を入れた方が堅牢だと思うのですが）

```shell-session:GIMPを検索してみる
$ cd /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/
$ ./lsregister -dump | grep --only-matching "/.*GIMP.app" | head -1
/Volumes/External_HDD/Applications/GIMP/GIMP_v2.8/GIMP.app
```

:thumbsup: bashり！

さぁ、未来の俺よ。ここまで調べたことを覚えていられるかな？(￣ー+￣)ﾆﾔﾘ…

## 参考文献

- [On macOS, how can I find the app's path which opens with “open” command?](https://stackoverflow.com/questions/51296900/on-macos-how-can-i-find-the-apps-path-which-opens-with-open-command#51618485) @ StackOverflow 自己回答
- [How do I find the path to a program in Terminal?](https://superuser.com/questions/570112/how-do-i-find-the-path-to-a-program-in-terminal#570120) | SuperUser @ StackExchange
- [AppleScriptのファイル参照にまつわるメモ](http://d.hatena.ne.jp/zariganitosh/20100924/apple_script_alias_posix_file) @ ザリガニが見ていた...