macOSでBashスクリプトを常駐プロセスとしてログイン時に起動させる

# はじめに

以前、下記の通り、ディレクトリを監視してGitにコミットするメールアドレスを変更するスクリプトを作成しました。

- [ディレクトリを定期的に監視してGitリポジトリに間違ったEmailをコミットしない - Qiita](http://qiita.com/artifactsauce/items/10141d6473bbb0e64cb1)

このスクリプトは非常に便利なので、忘れずに常に起動していてほしいと思っています。できればシステムの起動またはログインと同時に常駐プロセスとして起動したいですね。そこで今回は macOS でこのプログラムを常駐プロセスにする方法を検討します。

# 方針

## LaunchAgents で起動する

macOSには [`launchd`](http://www.launchd.info/) というサービス管理フレームワークが搭載されています。 `launchd` は `init` や `inetd` 、 `crond` などの置き換えを目的としたもので、システム起動時やログイン時におけるプロセスの起動や定期実行を行うことができます。詳しい説明は他の記事に譲りますが、ここではユーザーレベルのプロセスの起動のためにはこの仕組みを利用するべきとだけ理解していただければ十分です。

`launchd` の設定ファイルは通称 `launchd.plist` と呼ばれます。 `.plist` はプロパティリストと呼ばれるmacOS全般で使われる設定ファイルの拡張子で、現在ではXML形式で記述されています。

# 実装

そんなこんなで、下記のようなプロパティリストを作成しました。このファイルを任意のファイル名（ここでは `watch-git-email.plist` ）で `~/Library/LaunchAgents/` に保存してください。

```xml:~/Library/LaunchAgents/watch-git-email.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>watch-git-email</string>
    <key>ProgramArguments</key>
    <array>
        <string>/ABSOLUTE/PATH/TO/watch-git-email.sh</string>
        <string>/ABSOLUTE/PATH/TO/PARENT/DIRECTROTY</string>
        <string>expect-email@example.com</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
```

十分とは言えませんが、これで必要な項目は揃っています。簡単ですね。

## 実行

プロセスを起動させるためには下記のコマンドを実行します。 `launchctl` は `launchd` のための管理コマンドと理解しておけばだいたい大丈夫です。

```console
$ launchctl load ~/Library/LaunchAgents/watch-git-email.plist
```

また、このディレクトリに保存しておけば、ログイン時に自動的にユーザー権限で実行されます。

## 停止

プロセスの停止には下記のコマンドを実行します。起動時との違いはサブコマンドが `load` → `unload` になるだけです。

```console
$ launchctl unload ~/Library/LaunchAgents/watch-git-email.plist
```

# 解説

## `launchd.plist`

設定ファイルの記述内容について軽く解説していきます。今回の目的に際してはこの程度の記述で実行できますが、もっと詳細に管理したいときは、ぜひ[launch.plistのマニュアルページ](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html)でご確認ください。

`launchd` では登録されたプログラムを実行する単位を「ジョブ」と呼んでいます。ジョブ1つに対してプロパティリストが1つと考えてください。

### ジョブの名称 - `Label`

ジョブには固有の名称をつける必要がありますので、これは必須項目です。この `Label` の値はファイル名と同一であることが期待されているようです。つまりファイル名は `<Label>.plist` ということですね。前述のファイルのファイル名もそのようになっていることに注目してください。

```xml
<key>Label</key>
<string>watch-git-email</string>
```

### 実行するコマンド - `ProgramArguments`

プログラムの実行方法を記述する項目です。コマンドと引数をそれぞれ配列の別要素として記述します。 `$HOME` などの環境変数には期待せず、絶対PATHを記載してください。相対PATHで記載する方法もありますが、ここでは単純化のためにこうしてください。引数を取らない場合は `Program` というkeyを使うことができ、当然ながら `ProgramArguments` か `Program` のどちらかが記載されている必要があります。

```xml
<key>ProgramArguments</key>
<array>
    <string>/ABSOLUTE/PATH/TO/watch-git-email.sh</string>
    <string>/ABSOLUTE/PATH/TO/PARENT/DIRECTROTY</string>
    <string>expect-email@example.com</string>
</array>
```

### ロード時に実行する - `RunAtLoad`

`launchd` がプロパティリストをロードした時に実行するか否かを指定します。デフォルトの挙動では `launchd` がロードした時にはジョブを登録するだけで、実行はしません。ロードした時に実行させたい場合は、この値を `true` にする必要があります。

`crond` のように定期実行させる場合には、ロード時には実行させる必要がないので、記載する必要はありません。（デフォルトの挙動になる）

```xml
<key>RunAtLoad</key>
<true/>
```

# 終わりに

`launchd start` というサブコマンドとか、保存するディレクトリによって起動するタイミングなどが違うといったことも説明したかったという気持ちもありますが、できるだけシンプルに説明するためにけっこうな部分を省きました。プログラムの自動起動が成功したら、改めて公式ドキュメントを読むことをおすすめします。