0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Mac の launchd / launchctl から display notification で通知を飛ばす

Last updated at Posted at 2025-02-25

背景

Macの標準機能の時報は時間を言ってくれる機能しかないが、音を消している時でも気づけるようにバナー通知を飛ばしたい。
ただそれだけなのにドはまりしたので困っている人のためになればと思い書いた。

概要

Mac は cron も使えるけど launchd を推奨。

launchd に登録するには plist を作成して launchctl コマンドを叩く。

通知を飛ばすには AppleScript で display notification を実行する。

AppleScript を動かすには osascript コマンドを使う。

AppleScript から通知を飛ばすには スクリプトエディタ.app で一度 display notification を実行して、環境設定の通知で通知をオンにしなければならない。

launchd から osascript コマンドを実行するなら plist の ProgramArguments で osascript を直接書くか、別途作ったシェルスクリプトを書いて実行権限を付与しておけばいい。

シェルスクリプトを用意して /bin/bash の引数でシェルスクリプトを指定する場合、環境設定のプライバシーとセキュリティで /bin/bash にディスクアクセスの権限を付与する必要がある(対象のディレクトリでもいいし、フルディスクアクセスでもいいが対象のディレクトリは画面から指定する方法が見当たらない)。

※なお、brew install terminal-notifierでもOK。これなら権限とか気にせずサクッと動いた。今回はroot権限もらえてない会社パソコンでの作業なので osascript に固執した。

シェルスクリプトに実行権限を与えて直接起動(plist の ProgramArguments で /path/to/hoge.sh を指定)する方法ならディスクアクセスの権限は不要だが、下記手順では bash に引数でスクリプト名を渡す方法で書いておく。

手順

シェルスクリプト作成

show.sh
#!/bin/bash
hour=`expr "$(date +%H)"`
time=$(date +%H:%M)

if [[ $((hour)) -gt 7 && $((hour)) -lt 22 ]] ; then

/usr/bin/osascript -e 'display notification "" with title  "'$time'" '

fi

実行権限付与(やらなくてもいいが)

sh
chmod u+x show.sh

この時点では動かしても通知は出ない。

スクリプトエディタ.app の通知設定

スクリプトエディタ.appを開いてdisplay notification を実行する。

image.png

▶を押して実行すると、環境設定の通知の画面に「スクリプトエディタ」が出てくるので通知を ON にする。
image.png

これでターミナルなどで

osascript -e 'display notification "Hello!"'

などとやると通知が出るようになる。

plist作成

org.USER_NAME.hourlyAlerm.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>org.USER_NAME.hourlyAlerm</string>

    <!-- 実行するコマンドと引数 -->
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/USER_NAME/Documents/scripts/hourlyAlerm/show.sh</string>
    </array>

    <key>StartCalendarInterval</key>
    <dict>
        <key>Minute</key>
        <integer>0</integer>
    </dict>

    <!-- 標準出力・標準エラーの出力先 -->
    <key>StandardOutPath</key>
    <string>/Users/USER_NAME/Library/Logs/org.USER_NAME.hourlyAlerm.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/USER_NAME/Library/Logs/org.USER_NAME.hourlyAlerm.err.log</string>
</dict>
</plist>

USER_NAME や hourlyAlerm はただのJOB名やPATHなのでお好きなものに書き換えて。

PATH は通っていない可能性大なので launchctl setenv PATH 〜〜 で PATH を設定するかすべてフルパスで書く。

bash からシェルスクリプトを読み込めるようにするため、環境設定のプライバシーとセキュリティでフルディスクアクセスか対象のディレクトリへのアクセス権を与える

image.png

↓ Shift + Command + . で /bin などの隠しファイルも表示できる。

image.png

image.png

これで plist に

<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/path/to/hoge.sh</string>
</array>

ってしたときに bash が hoge.sh を読み込んで実行できるようになる。

なお、logの出力先を /Users/USER_NAME/Documents/ などの下にするとlaunchctl list で見たときの Status が 78 になって動かない。bash にディスクフルアクセス与えてもダメだった。
フルディスクアクセスを付与する際に、自分で作った hoge.sh は選ぶことが出来なかった。

launchctl で登録して動作確認

いまどきは load/unload ではなく bootstrap/bootout らしい。

sh
launchctl bootstrap gui/$(id -u) org.USER_NAME.hourlyAlerm.plist

ここで環境設定の「ログイン項目と機能拡張」に bash が登録されたと通知が出る。

これで対象の時間になったら通知が出るはず。

なんかおかしかったら launchctl list で出る Status が 0 以外の数字になっている。でもその数字が何を意味するのかググってもたどり着けなかった。

↓構文チェックは

sh
plutil -lint org.USER_NAME.hourlyAlerm.plist

↓これで launchd のログが見られるらしいが今回は役に立たなかった

sh
log stream --predicate 'subsystem == "com.apple.launchd"' --info
tail -f /var/log/system.log
launchctl print gui/$(id -u)/org.USER_NAME.hourlyAlerm

↓plist に書けば Debug 情報が出せるらしいが今回は役に立たなかった

plist
    <key>Debug</key>
    <true/>

plistを修正して反映させたいときは、一度 bootoutで登録解除してから再登録する。

sh
#↓まず登録解除して、
launchctl bootout gui/$(id -u) org.USER_NAME.hourlyAlerm.plist
#エディタで修正して、
vi org.USER_NAME.hourlyAlerm.plist
#↓再登録
launchctl bootstrap gui/$(id -u) org.USER_NAME.hourlyAlerm.plist

※bootout する前に plist 消しちゃうった場合

sh
launchctl remove gui/$(id -u)/org.USER_NAME.hourlyAlerm
#もしくは
launchctl bootout gui/$(id -u)/org.USER_NAME.hourlyAlerm

自分が使うときだけ動けばいいので ~/Library/LaunchAgents/ の中にいれる

sh
mkdir ~/Library/LaunchAgents/
cp org.USER_NAME.hourlyAlerm.plist ~/Library/LaunchAgents/

launchctl bootstrap するだけでは再起動後に登録解除されてしまっている。ここに入れておけば再起動後も読み込まれる。

用途によって保存場所が異なるので参考までに↓

保存場所 用途
~/Library/LaunchAgents ユーザー権限で動くユーザごとのエージェント
/Library/LaunchAgents 管理者権限で動くユーザごとのエージェント
/Library/LaunchDaemons 管理者権限で動くシステム全体的なデーモン
/System/Library/LaunchAgents OSの一部として動くユーザごとのエージェント
/System/Library/LaunchDaemons OSの一部として動くシステム全体的なデーモン

ユーザー権限でない場合は launchctl bootstrap するときに sudo が必要で、オプションも gui ではなく system になる。

sh
sudo launchctl bootstrap system org.USER_NAME.hourlyAlerm.plist

あと、さっきまで動いていたのに突然動かなくなって困り果てていたら、22時過ぎて集中モードのおやすみモードに入って通知がオフになってただけだったという罠もあった。

参考:
Apple公式資料 Macの「ターミナル」でのlaunchdを使ったスクリプトの管理
これ以上の詳細は man page にしかないっぽい。man page にもStatus codeなどは記載されていない。

おしまい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?