ページ

2014/04/24

[Mac][zsh]時間のかかるコマンドが終わったら自動で通知する

巨大なプロジェクトの clone とか時間のかかるコマンドを実行したりすると
待ってるのが暇なので他のことをやり始めたりするんですが、
気がついたらコマンドが実行完了してて時間を浪費してしまうので
コマンドが終わったら自動で通知してくれるようにしました。

参考: Macで時間のかかるコマンドが終わったら、自動で通知するzsh設定 - Qiita

Requirements

Mac OS X 10.8 以上
Terminal.app または iTerm2.app

Terminal-notifier.app をインストールする

alloy/terminal-notifier

Download

Prebuilt binaries are available from the releases section.

Or if you want to use this from Ruby, you can install it through RubyGems:
[sudo] gem install terminal-notifier

You can also install it via Homebrew:
brew install terminal-notifier
HomeBrew で インストールしようと思ったらフォルダ構成が変わったのか 404 になってしまったので
Releases · alloy/terminal-notifier から緑のボタンの zip をダウンロードしました。
解凍して /Applications にDrag&Drop でインストール完了です。

zsh-notify をインストールする

marzocchi/zsh-notify を zip でダウンロードしてきて解凍して ~/.zsh/ とかに入れます。
以下のように ~/.zshrc に記述して zsh-notify を有効にします。
# add-zsh-hook を有効にする | zsh-notify を有効にする
autoload -Uz add-zsh-hook
source ~/.zsh/zsh-notify/notify.plugin.zsh
参考: zsh-notify x Growl - 人生いきあたりばったりで生きてます@はてな

zsh-notify をカスタマイズする

SYS_NOTIFIER に Terminal-notifier のパスを指定したり、
NOTIFY_COMMAND_COMPLETE_TIMEOUT(※)で閾値を変更したり、
通知のタイトルを変えたりしてみました。
(※実行時間がこの秒数を超えたコマンドが実行完了した際に Terminal がバックグラウンドだったら通知される)
# vim: set nowrap filetype=zsh:
#
# See README.md.
#
fpath=($fpath `dirname $0`)

SYS_NOTIFIER="/Applications/terminal-notifier.app/Contents/MacOS/terminal-notifier"
NOTIFY_COMMAND_COMPLETE_TIMEOUT=3

# Default timeout is 30 seconds.
[[ $NOTIFY_COMMAND_COMPLETE_TIMEOUT == "" ]]  \
  && NOTIFY_COMMAND_COMPLETE_TIMEOUT=30

# Notify an error with no regard to the time elapsed (but always only
# when the terminal is in background).
function notify-error {
  local icon
  icon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
  notify-if-background -t "Failed!" --image "$icon" < /dev/stdin &!
}

# Notify of successful command termination, but only if it took at least
# 30 seconds (and if the terminal is in background).
function notify-success() {
  local now diff start_time last_command

  start_time=$1
  last_command="$2"
  now=`date "+%s"`

  ((diff = $now - $start_time ))
  if (( $diff > $NOTIFY_COMMAND_COMPLETE_TIMEOUT )); then
    notify-if-background -t "Success!" <<< "$last_command" &!
  fi
}

# Notify about the last command's success or failure.
function notify-command-complete() {
  last_status=$?
  if [[ $last_status -gt "0" ]]; then
    notify-error <<< $last_command
  elif [[ -n $start_time ]]; then
    notify-success "$start_time" "$last_command"
  fi
  unset last_command start_time last_status
}

function store-command-stats() {
  last_command=$1
  last_command_name=${1[(wr)^(*=*|sudo|ssh|-*)]}
  start_time=`date "+%s"`
}


autoload -U tell-terminal
autoload -U tell-iterm2
autoload -U notify-if-background
add-zsh-hook preexec store-command-stats
add-zsh-hook precmd notify-command-complete
こういうの読めばわかる気がしてくるので ShellScript 勉強しようって思います。
途中まで読んで積ん読になっている入門UNIXシェルプログラミングを読んで色々できるようになりたい。

変更を反映する

source ~/.zshrc
忘れずに。

通知をテストする

成功
sleep 31
実行時間が 30秒 (デフォルトの NOTIFY_COMMAND_COMPLETE_TIMEOUT の値)を超え、
バックグラウンドなら通知されます。
失敗
sleep 3 && ls nofile
実行時間が 30秒 (デフォルトの NOTIFY_COMMAND_COMPLETE_TIMEOUT の値)を超えなくても
失敗時は(バックグラウンドなら)通知されます。

大変便利です!