定期的にスクリプトを実行する場合、Mac OS Xではcrontab
よりlaunchd
を使うことが推奨されている。
launchd
を用いてMac OS Xで定期的にスクリプトを実行する方法を記述。
特徴
- 設定が2種類ある:
- エージェントはユーザーがログイン中に実行できるプログラム。
- デーモンはシステム共通で、誰もログインしていなくても実行できるプログラム。
- 前回の実行が終わらないと次の実行は始まらない。また、スリープ状態、シャットダウン状態では実行されない。1
- 標準でCPU時間、メモリ、ファイル等の使用の制限2が設けられている。必要に応じて設定ファイルで上限を上げないと、シグナルで届いてしまう。
設定ファイルを置く場所は次の通り。
場所 | 用途 |
---|---|
~/Library/LaunchAgents/<Label>.plist |
ユーザーごと設定できるエージェント |
/Library/LaunchAgents/<Label>.plist |
管理者用のエージェント |
/Library/LaunchDaemons/<Label>.plist |
システム共通のデーモン(root がオーナー) |
Label はユニークな識別名。ファイルはplist
形式3。
#単純なサンプル
一定間隔で実行と、特定の時間に実行する方法がある。
10秒間隔でスクリプトを実行
次を満たすサンプルを作る。
- 実行するのは一般ユーザー所有のスクリプト:
test.rb
。 - 10秒ごとに実行するエージェントとして設定する。
- STDOUT/STDERR出力をファイルに保存(
test.out
/test.err
)。
識別名(Label)はtest
にするので設定ファイルはtest.plist
。
一般ユーザー所有のエージェントスクリプトなので~/Library/LaunchAgents/
に置く。
<?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>test</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ruby</string>
<string>-Ku</string>
<string>/Users/mofu/script/test.rb</string>
</array>
<key>StartInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>/Users/mofu/script/test.out</string>
<key>StandardErrorPath</key>
<string>/Users/mofu/script/test.err</string>
</dict>
</plist>
次のコマンドで登録すると、実行されるようになる。
$ launchctl load ~/Library/LaunchAgents/test.plist
これ以降、システムを再起動してもこのユーザーでログインすれば設定が維持され、実行が再開する。
登録解除するには次のコマンドを使う。
$ launchctl unload ~/Library/LaunchAgents/test.plist
これ以降、実行されなくなる。
毎日、0時0分にスクリプトを実行
次を満たすサンプルを作る。
- 実行するのは一般ユーザー所有のスクリプト:
test2.rb
。 - 毎日、0時間0分に実行するエージェントとして設定する。
- STDOUT/STDERR出力をファイルに保存(
test2.out
/test2.err
)。
識別名はtest2
とするので設定ファイルはtest2.plist
。
一般ユーザー所有のエージェントスクリプトなので~/Library/LaunchAgents/
に置く。
<?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>test2</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ruby</string>
<string>-Ku</string>
<string>/Users/mofu/script/test2.rb</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>0</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/mofu/script/test2.out</string>
<key>StandardErrorPath</key>
<string>/Users/mofu/script/test2.err</string>
</dict>
</plist>
次のコマンドで登録。
$ launchctl load ~/Library/LaunchAgents/test2.plist
#用途ごとサンプル
iOSアプリのデイリービルドデーモン
次を満たすサンプルを作る。
- スクリプト
build.sh
をシステム共通で(ログインしていない状態でも)実行できるようにする。 - すなわちデーモンとして設定する。実行するタイミングは毎日0時間0分。
- デーモンとして設定するので
root
所有の設定だが、スクリプトの実行ユーザーはmofu
とする。 - 各種制限に引っかからないよう、上限を設定する。
- 処理時間:1,200秒(標準は20秒である)。
- CPU時間:1,200秒。
- 最大メモリ:2,000,000,000バイト。
- 最大ファイルサイズ:2,000,000,000バイト。
- 環境変数を設定:CocoaPodsの
pod install
のためLC_ALL
をen_US.UTF-8
に。 - ワーキングディレクトリを
/Users/mofu/script
に設定。 - STDOUT出力をファイルに保存(
build.out
)。
識別名はbuild
とするので設定ファイルはbuild.plist
。
システム共通のデーモンスクリプトなので/Library/LaunchDaemons/
に置き、root
を所有者にする。
<?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>build</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/mofu/script/build.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>0</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/Users/mofu/script/build.out</string>
<key>WorkingDirectory</key>
<string>/Users/mofu/script</string>
<key>EnvironmentVariables</key>
<dict>
<key>LC_ALL</key>
<string>en_US.UTF-8</string>
</dict>
<key>UserName</key>
<string>mofu</string>
<key>SessionCreate</key>
<true />
<key>ExitTimeOut</key>
<integer>1200</integer>
<key>SoftResourceLimits</key>
<dict>
<key>CPU</key>
<integer>1200</integer>
<key>FileSize</key>
<integer>2000000000</integer>
<key>Data</key>
<integer>2000000000</integer>
</dict>
<key>HardResourceLimits</key>
<dict>
<key>CPU</key>
<integer>1200</integer>
<key>FileSize</key>
<integer>2000000000</integer>
<key>Data</key>
<integer>2000000000</integer>
</dict>
</dict>
</plist>
次のコマンドで設定ファイルのオーナーをroot
にし、登録する。
$ sudo chown root /Library/LaunchDaemons/build.plist
$ sudo launchctl load /Library/LaunchDaemons/build.plist
※iOSビルドスクリプトについてはこちらに記述。
平日のみ、毎日10時にスクリプトを実行
次を満たすサンプルを作る。
- スクリプト
weekdays.sh
をデーモンとして実行する。 - 実行するタイミングは平日のみ、毎日10時。
<?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>weekdays</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/mofu/script/weekdays.sh</string>
</array>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Weekday</key>
<integer>1</integer>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>2</integer>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>3</integer>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>4</integer>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Weekday</key>
<integer>5</integer>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
</dict>
</plist>
次のコマンドで設定ファイルのオーナーをroot
にし、登録する。
$ sudo chown root /Library/LaunchDaemons/weekdays.plist
$ sudo launchctl load /Library/LaunchDaemons/weekdays.plist
#設定項目の一覧
定期的にスクリプトを実行するために使う設定。
識別名【必須】
ジョブを識別するためのユニークな文字列で構成された識別名をLabel
に指定する。
ファイル名にもこの文字列を使う。
<key>Label</key>
<string>mofu.build.daily</string>
何を実行するか【必須】
ProgramArguments
に、実行するプログラムとパラメータを指定する。
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/mofu/script/build.sh</string>
</array>
いつ実行するか
一定間隔ならStartInterval
で秒数を指定する。
<key>StartInterval</key>
<integer>10</integer>
特定の時間に実行するならStartCalendarInterval
に時間を指定する。
-
Minute
:分 -
Hour
:時間 -
Day
:日 -
Weekday
:曜日(0と7は日曜日) -
Month
:月
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>0</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
複数の時間を指定することもできる。
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<dict>
<key>Hour</key>
<integer>19</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
</array>
どの環境で実行するか
WorkingDirectory
にワーキングディレクトリを指定できる。
<key>WorkingDirectory</key>
<string>/Users/mofu/script</string>
EnvironmentVariables
に、環境変数を指定できる。
<key>EnvironmentVariables</key>
<dict>
<key>LC_ALL</key>
<string>en_US.UTF-8</string>
</dict>
UserName
に、スクリプトを実行するユーザーを指定できる。
<key>UserName</key>
<string>mofu</string>
そのユーザーのセッションを作成した上で実行したい場合はSessionCreate
を指定できる。(デーモンでもログインキーチェインが使える状態になる。)
<key>UserName</key>
<string>mofu</string>
<key>SessionCreate</key>
<true />
処理時間の制限
ExitTimeOut
に処理の制限時間を秒数で設定できる。標準は20秒。
この時間が経過するとプロセスにSIGKILL
シグナルが送られる。
<key>ExitTimeOut</key>
<integer>1200</integer>
リソースの制限
SoftResourceLimits
、HardResourceLimits
に、ソフト制限、ハード制限を設定できる。
ソフト制限を超えるとプロセスにシグナルが送られるがハート制限未満までなら実行を続けられる。
主な設定項目4:
-
CPU
:各プロセスに使用される最大CPU時間(秒)。 -
Data
:使用できる最大メモリ(バイト)。 -
FileSize
:作成できるファイルの最大サイズ(バイト)。
<key>SoftResourceLimits</key>
<dict>
<key>CPU</key>
<integer>1200</integer>
<key>FileSize</key>
<integer>2000000000</integer>
<key>Data</key>
<integer>2000000000</integer>
</dict>
<key>HardResourceLimits</key>
<dict>
<key>CPU</key>
<integer>1200</integer>
<key>FileSize</key>
<integer>2000000000</integer>
<key>Data</key>
<integer>2000000000</integer>
</dict>
#参照
- Scheduling Timed Jobs
- Creating Launch Daemons and Agents
- MAN: launchd(8)
- MAN: launchd.plist(5)
- A launchd Tutorial
- SessionCreateプロパティの使用 - StackOverflow
- Technical Note TN2083 Daemons and Agents(SessionCreateプロパティが載っている)