予約日時になったらちゃんと目覚めるtimerコマンドが欲しい

前回radikoらじる★らじるを録音するrec_radikoru.appを作った。どうにか録音することはできるようになったのだけど...じゃあ、その他のネットラジオを録音したいとか、iTunesを目覚まし代わりに起動したいとか、その他いろいろな欲求が湧いてきた時に、現状のrec_radikoru.appではどうにもならない...。

ところで、OSXにも時間管理の仕組みは用意されている。システム環境設定 >> 省エネルギーで、繰り返しのスケジュールを設定できる。でも設定できることは、起動・スリープ解除か、終了・スリープだけ。起動したり、スリープ解除するということは、その時点で実行したいことがあるはずなんだけど、何を実行するかまでは指定できないのだ。設定できる繰り返しスケジュールも起動系と終了系でそれぞれ1つだけ。

何を実行するかの予約は、launchdやcron、それにカレンダーイベントなどを使えば設定できる。ただし、それらがちゃんと機能するためにはmacが覚醒していなければならない。スリープ中だと、予約した時間になっても何もしてくれないのだ...。それを回避するためには、スリープしない設定で運用してしまう、という手もある。(何の苦労もなく、すべてが解決してしまう)

でも、せっかく備わっている高度なスリープ関連の機能を無視してしまうことは、あまり好まない。何のために、Wake On DemandPower Napはあるのか。やはり、使っていない時は正しくスリープして消費電力を最小に押さえ、必要に応じてスリープを解除して、必要な処理を行うという姿勢がベストである。

しかし、スリープ解除と連携したタイマー駆動の仕組みは、OSX環境には用意されていない*1。(と思っている)自分でpmsetを使って、設定したタイマーと連携するように事前に設定しておく必要がある。これでは非常に面倒である。高度なインターフェースを備えたOSなのに、タイマー機能については泣けてくるくらいお粗末である。

rec_radikoru.appで実現したスリープ解除と連携したタイマー駆動の仕組みを、もっと汎用的に利用できるようにしておきたい。テレビやラジオの録画予約程度のことを実現するのに手間のかかる現状を、少しでも改善したいのだ。

timerコマンドプロジェクトの開始

  • 汎用的に使うためには、アプリケーションにするよりコマンドにしておいた方がいい。
  • コマンドならほとんどのスクリプト言語から簡単に呼び出せるし、アプリケーションに組み込むこともできる。
  • 理想は、cronのようにcrontabを利用して設定できるのがベストなのだが、cronレベルのものを作る技術は自分にはない。
  • そもそも、スリープ解除と連携させるのだから、毎時単位のタイマー機能は不要である。
  • そんなに頻繁に実行したいものがあるのなら、スリープしない設定にして、cronを使えばいいのだ。
  • 求めるのは毎日単位より間隔の長いタイマー機能である。
  • それから、毎年単位のタイマー機能も不要である。
  • そんなに長い間隔のタイマーで何時何分まで指定する必要性はほとんどない。
  • 素直にカレンダーに繰り返しの予定を入れておけば、きっと満足できる。

よって、必要なのは日単位以上、年単位未満のスリープ解除と連携したタイマー機能である。

タイマーの設定フォーマット

  • これはもう素直にcronを見習う。
  • でもcronほど細かな制御は必要ないので、曜日と時刻、日付と時刻の二つの引数とそれに続くコマンドを実行するようにしたい。
  • 毎週日曜の17時からのJ-WAVE サウジ・サウダージを録音したい場合は、以下のように指定できると良さそう。
    • 0から6の数字が、日から土の曜日に対応する。
0 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
  • それから、月曜から金曜とか、月水金などの指定もcronを見習って、できるようにしたい。
1-5 0600 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
1,3,5 0600 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
  • また、曜日でなく日付指定をする場合はこんな感じ。
  • 2/3は日曜日なので、曜日0を指定したタイマーと同じくサウジ・サウダージが録音されるはず。
2/3 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
  • ただし、曜日指定は週単位の繰り返しタイマーだが、日付指定はその日限りの単発のタイマーとしたい。
  • そうしておけば、自分の使い方の範囲ではほとんどの欲求が満たされそう。
  • 週単位の時刻指定の繰り返しはあるけど、月単位の時刻指定の繰り返しはほとんどないので。
  • 月単位の繰り返しなら、カレンダーにイベントを設定しておくだけで十分という判断。

timerコマンド

  • まず必要になるのは、timerコマンド。
  • CUIのインターフェースを提供して、予約設定の処理を行うのだ。
  • 予約設定はlaunchd.plistに変換される。
  • 例えば、以下のようなコマンド入力によって...
timer -e 0 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
  • 毎週日曜の17時に起動させるlaunchd.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>com.bebekoubou.timer-week 0 1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ</string>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/bin/caffeinate</string>
		<string>/bin/sh</string>
		<string>-c</string>
		<string>/usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ; /usr/local/bin/timer-sync-wake;</string>
	</array>
	<key>StartCalendarInterval</key>
	<array>
		<dict>
			<key>Hour</key>
			<integer>17</integer>
			<key>Minute</key>
			<integer>0</integer>
			<key>Weekday</key>
			<integer>0</integer>
		</dict>
	</array>
</dict>
</plist>
  • と、ここまでは前回作ったradiko・らじる録音専用のrec_radikoru.appで実現できていたのだけど、
  • timerコマンドはさらに「1-5」とか「1,3,5」などの複数の曜日指定もサポートしている。
  • さらには「2/3」などの日付指定もサポートし、日付指定の場合は繰り返さない単発のタイマー予約としている。
  • 地味な機能だけど、そうゆうことを実現するのは意外と面倒である。
  • 悩みながら書いたコードはこんな感じになった。(200行くらい)
#!/bin/bash
IDENTIFIER=com.bebekoubou.timer

weekdays() {
  safe_chars=`echo $1|sed 's/[^0-9,-\/]//g'`
  bash_format=`echo $safe_chars|sed 's/\(.-.\)/{\1}/g'|sed 's/-/../g'|tr ',' ' '`
  bash -c "echo $bash_format"|tr ' ' '\n'|sort -u
}

formed_args() {
  m=`echo $1|grep /|cut -d/ -f1|xargs printf %02s`
  d=`echo $1|grep /|cut -d/ -f2|xargs printf %02s`
  w=`echo $1|grep -v /`
  hhmm=`echo $2|sed s/://|xargs printf %04s`
  cmd=`echo $@|cut -d' ' -f3-`
  echo -n ${w:-$m/$d} $hhmm $cmd
}

week_or_date() {
  echo $1|grep -q / || echo week
  echo $1|grep -q / && echo date
}

plist_fname() {
  echo -n $IDENTIFIER-`week_or_date $1` `formed_args $@|sed 's/\//_/g'`
}

launchd_path() {
  echo -n $HOME/Library/LaunchAgents/`plist_fname $@`.plist
}

plist_Label() {
  echo -n $IDENTIFIER-`week_or_date $1` `formed_args $@`
}

plist_StartCalendarInterval_week() {
  hh=`echo $2|sed s/://|xargs printf %04s|cut -c 1-2`
  mm=`echo $2|sed s/://|xargs printf %04s|cut -c 3-4`
  for w in `weekdays $1`
  do
    echo "<dict><key>Hour</key><integer>$hh</integer><key>Minute</key><integer>$mm</integer><key>Weekday</key><integer>$w</integer></dict> "
  done
}

plist_StartCalendarInterval_date() {
  m=`echo $1|cut -d/ -f1`
  d=`echo $1|cut -d/ -f2`
  hh=`echo $2|sed s/://|xargs printf %04s|cut -c 1-2`
  mm=`echo $2|sed s/://|xargs printf %04s|cut -c 3-4`
  echo "<dict><key>Month</key><integer>$m</integer><key>Day</key><integer>$d</integer><key>Hour</key><integer>$hh</integer><key>Minute</key><integer>$mm</integer></dict> "
}

plist_StartCalendarInterval() {
  echo $1|grep -q / || plist_StartCalendarInterval_week $@
  echo $1|grep -q / && plist_StartCalendarInterval_date $@
}

plist_ProgramArguments_week() {
  shift 2
  echo "$@; /usr/local/bin/timer-sync-wake;"
}

plist_ProgramArguments_date() {
  launchd_plist=`launchd_path $@`
  launchd_label=`plist_Label $@`
  shift 2
  echo "$@; rm -f '$launchd_plist'; /usr/local/bin/timer-sync-wake; launchctl remove '$launchd_label'"
}

plist_ProgramArguments() {
  echo $1|grep -q / || plist_ProgramArguments_week $@
  echo $1|grep -q / && plist_ProgramArguments_date $@
}

list_launchd() {
  echo ======== launchd list >&2
  launchd_plist_paths=`ls $HOME/Library/LaunchAgents/$IDENTIFIER* 2>/dev/null`
  status=$?
  local IFS=$'\n'
  for f in $launchd_plist_paths
  do
    echo -e `defaults read "${f%.plist}" Label|cut -d' ' -f2-|sed 's/ /\\\t/'`|expand
  done
  return $status
}

list_schedule() {
  echo ======== schedule list
  pmset -g sched
}

add_launchd() {
  echo ======== edit
  launchd_plist=`launchd_path $@`
  defaults write "${launchd_plist%.plist}" Label "`plist_Label $@`"
  defaults write "${launchd_plist%.plist}" ProgramArguments -array /usr/bin/caffeinate /bin/sh -c "`plist_ProgramArguments $@`"
  defaults write "${launchd_plist%.plist}" StartCalendarInterval -array `plist_StartCalendarInterval $@`
  launchctl load "$launchd_plist"
  echo -e `echo $@|sed 's/ / \\\t/'`
}

rm_launchd() {
  echo ======== delete
  launchd_plist=`launchd_path $@`
  launchctl unload "$launchd_plist"
  rm -f "$launchd_plist"
  echo -e `echo $@|sed 's/ / \\\t/'`
}

show_usage() {
  echo "Usage: $COMMAND [-li]"
  echo "       $COMMAND [-de] mm/dd HHMM command; ..."
  echo "       $COMMAND [-de] weeknum HHMM command; ..."
  echo "           mm/dd  : 02/01=2/1"
  echo "           weeknum: 0=日 1=月 2=火 3=水 4=木 5=金 6=土 7=日"
  echo "                      1-5 = 月から金まで"
  echo "                      5-7 = 金土日"
  echo "                    1,3,5 = 月水金"
  echo "           HHMM   : 0600=6:00"
  echo "Example:"
  echo "       $COMMAND -e 02/01 1700 /usr/local/bin/rec_radiko.sh -o radikoru/ -t 3600 FMJ"
  echo "       $COMMAND -d 02/01 1700 /usr/local/bin/rec_radiko.sh -o radikoru/ -t 3600 FMJ"
  echo "       $COMMAND -e 0 1700 /usr/local/bin/rec_radiko.sh -o radikoru/ -t 3600 FMJ"
  echo "       $COMMAND -e 1-5 0600 /usr/local/bin/rec_radiru.sh -o radikoru/ -t 1800 NHK-FM"
}

command_v() {
  osascript -e 'delay .5' -e 'tell application "System Events"' -e 'keystroke "v" using command down' -e 'end tell'
}




cd `dirname $0`
COMMAND=`basename $0`

# 引数解析
while getopts deli OPTION
do
  case $OPTION in
    d ) OPTION_d="TRUE" ; VALUE_d="$OPTARG" ;;
    e ) OPTION_e="TRUE" ; VALUE_e="$OPTARG" ;;
    l ) OPTION_l="TRUE" ;;
    i ) OPTION_i="TRUE" ;;
    * ) show_usage ; exit 1 ;;
  esac
done

shift $(($OPTIND - 1)) #残りの非オプションな引数のみが、$@に設定される

if [ $# = 0 -a "$OPTION_l" != "TRUE" -a "$OPTION_i" != "TRUE" ]; then
  show_usage ; exit 1
fi

if [ $OPTIND = 1 ]; then
  echo Needs option!! ; show_usage ; exit 1
fi

# オプション処理
if [ "$OPTION_d" = "TRUE" ]; then
  rm_launchd $@
  ./timer-sync-wake
  list_launchd
  list_schedule
fi

if [ "$OPTION_e" = "TRUE" ]; then
  add_launchd $@
  ./timer-sync-wake
  list_launchd
  list_schedule
fi

if [ "$OPTION_l" = "TRUE" ]; then
  list_launchd
  list_schedule
fi

if [ "$OPTION_i" = "TRUE" ]; then
  list_launchd &>/dev/null || exit 1
  list_launchd 2>/dev/null|nl -s':  '
  
  read -p 'Select Number :' NUM
  echo -n $NUM|grep -q '[0-9]\{1,\}' || exit 1
  [ $NUM -gt `list_launchd 2>/dev/null|wc -l` ] && exit 1
  command_args=`list_launchd 2>/dev/null|head -$NUM|tail -1|sed 's/ \{2,\}/ /'`
  
  read -p '[c]opy  [d]elete :' ACTION
  case $ACTION in
    c) echo -en "$0 -e $command_args"|pbcopy
       command_v & ;;
       
    d) rm_launchd $command_args
       ./timer-sync-wake
       list_launchd
       list_schedule ;;
       
    *) exit 1;;
  esac  
fi

スリープ解除を連携させるtimer-sync-wakeコマンド

  • 設定したlaunchd.plistは、pmset scheduleと連携させることで、予約した時刻にスリープが解除され、指定したコマンドが実行される仕組み。
  • 繰り返し設定ができないpmset scheduleを、毎週繰り返しのlaunchd.plistと連携させるため、launchd.plistに設定した処理の最後で、次週のpmset scheduleを設定している。
  • そのlaunchd.plistとpmset scheduleの設定を同期させる仕事をするのが、このtimer-sync-wakeコマンドである。
  • timerコマンドで指定された曜日指定の予約処理は、launchd.plistに変換される際に、その最後に必ずこのtimer-sync-wakeコマンドが付加される。
  • timer-sync-wakeコマンドによって、次に必要なpmset scheduleが設定され、次週の予約日時のスリープ解除に備えるのである。
  • 以上の処理は、timerコマンドで予約した時に自動的に設定されるので、ユーザー自らがtimer-sync-wakeコマンドを実行する必要性はないのである。
#!/bin/sh
IDENTIFIER=com.bebekoubou.timer

pmdate_from_w_hhmm() {
  w=`expr $1 % 7`
  hh=`echo $2|sed s/://|xargs printf %04s|cut -c 1-2`
  mm=`echo $2|sed s/://|xargs printf %04s|cut -c 3-4`
  current_date=`date +%s`
  preset_date=`date -v${w}w -v${hh}H -v${mm}M -v0S +%s`
  n=`expr $current_date / $preset_date`
  date -v${w}w -v+${n}w -v${hh}H -v${mm}M -v0S -v-30S "+%m/%d/%y %H:%M:%S"
}

pmdate_from_md_hhmm() {
  m=`echo $1|cut -d/ -f1`
  d=`echo $1|cut -d/ -f2`
  hh=`echo $2|sed s/://|xargs printf %04s|cut -c 1-2`
  mm=`echo $2|sed s/://|xargs printf %04s|cut -c 3-4`
  current_date=`date +%s`
  preset_date=`date -v${m}m -v${d}d -v${hh}H -v${mm}M -v0S +%s`
  n=`expr $current_date / $preset_date`
  date -v+${n}y -v${m}m -v${d}d -v${hh}H -v${mm}M -v0S -v-30S "+%m/%d/%y %H:%M:%S"
}

add_schedule() {
  pmset -g sched|grep -q "wake at $1" || ./timer-add-schedule "$1"
}

rm_schedule() {
  pmset -g sched|grep -q "wake at $1" && ./timer-rm-schedule "$1"
}

weekdays() {
  safe_chars=`echo $1|sed 's/[^0-9,-\/]//g'`
  bash_format=`echo $safe_chars|sed 's/\(.-.\)/{\1}/g'|sed 's/-/../g'|tr ',' ' '`
  bash -c "echo $bash_format"|tr ' ' '\n'|sort -u
}




echo ======== sync-wake
cd `dirname $0`

_IFS="$IFS"; IFS=$'\n';

# launchd.plistを見て、不足するscheduleを追加する
launchd_plist_paths="`ls $HOME/Library/LaunchAgents/$IDENTIFIER* 2>/dev/null`"
for f in $launchd_plist_paths
do
  args=`defaults read "${f%.plist}" Label`
  word=`echo $args|cut -d' ' -f2`
  hhmm=`echo $args|cut -d' ' -f3`
  for wd in `weekdays $word`
  do
    if `echo $wd|grep -q /`; then
      pmdate=`pmdate_from_md_hhmm $wd $hhmm`
    else
      pmdate=`pmdate_from_w_hhmm $wd $hhmm`
    fi
    add_schedule "$pmdate"
    launchd_pmdate_list+="${pmdate}\n"
  done
done

# launchd.plistに存在しない、余分なscheduleを削除する
schedule_wake_list=`pmset -g sched|grep '^\W\['|grep 'wake at'|cut -d' ' -f6-7`
for s in $schedule_wake_list
do
  echo $launchd_pmdate_list|grep -q "$s" || rm_schedule "$s"
done

IFS="$_IFS"

その日限りの単発予約にする方法

  • 今度は逆に日付指定の繰り返さない予約も設定しなくてはならない。
  • launchdのStartCalendarIntervalは一旦設定がloadされると、その設定がある限り、永遠に繰り返す。
  • 例えば2月3日を設定しておくと、それは毎年繰り返されることになる。
  • その繰り返しを止めるために、予約処理の最後でunloadして、不要になったlaunchd.plistを削除しようとした。
  • ところが、launchd.plistによって起動された処理Aの中で、そのlaunchd.plistをunloadしてしまうと、unloadした瞬間にその処理Aも中止されてしまうようなのだ。
    • その後に続くlaunchd.plistの削除の処理が実行されず、ずっと残ったままになってしまう...。
  • 一方、unloadするためには、その設定ファイルであるlaunchd.plistが必要である。先に削除してしまうとunloadもできなくなる。
  • それなら削除するlaunchd.plistとは無関係の、定期的に監視して削除する処理Bを追加するしかないか...と悩んでいたら、launcdctlの中にremoveがあることに気付いた!
    • removeなら、稼働中のジョブをLabel指定で削除できる。先にlaunchd.plistを削除してしまってもOK。
timer -e 02/03 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ
  • 例えば、2/3の1700のタイマー予約をすると、以下のようなlaunchd.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>com.bebekoubou.timer-date 02/03 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ</string>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/bin/caffeinate</string>
		<string>/bin/sh</string>
		<string>-c</string>
		<string>/usr/local/bin/rec_radiko.sh -t 3600 FMJ; rm -f '/Users/zari/Library/LaunchAgents/com.bebekoubou.timer-date 02_03 1700 _usr_local_bin_rec_radiko.sh -t 3600 FMJ.plist'; /usr/local/bin/timer-sync-wake; launchctl remove 'com.bebekoubou.timer-date 02/03 1700 /usr/local/bin/rec_radiko.sh -t 3600 FMJ'</string>
	</array>
	<key>StartCalendarInterval</key>
	<array>
		<dict>
			<key>Day</key>
			<integer>3</integer>
			<key>Hour</key>
			<integer>17</integer>
			<key>Minute</key>
			<integer>0</integer>
			<key>Month</key>
			<integer>2</integer>
		</dict>
	</array>
</dict>
</plist>


これでひとまず、予約した時刻になったら、ちゃんとスリープを解除して、コマンドを実行するtimerコマンドの出来上がり!

rec_radikoru.shコマンド

#!/bin/bash

cd `dirname $0`

while getopts l OPTION
do
  case $OPTION in
    l ) OPTION_l="TRUE" ;;
  esac
done

if [ "$OPTION_l" = "TRUE" ]; then
  areaid=`/usr/local/bin/rec_radiko.sh -a|tail -1|cut -d, -f1`

  station_names=`echo ---- NAME ----,NHK-R1,NHK-R2,NHK-FM,`
  station_names+=`curl -s http://radiko.jp/v2/station/list/$areaid.xml|xpath //name 2>/dev/null|sed -e 's/<name>//g' -e 's/<\\/name>/,/g'`

  station_ids=`echo ---- ID ----,NHK-R1,NHK-R2,NHK-FM,`
  station_ids+=`curl -s http://radiko.jp/v2/station/list/$areaid.xml|xpath //id 2>/dev/null|sed -e 's/<id>//g' -e 's/<\/id>/,/g'`

  paste <(echo $station_ids|tr ',' '\n') <(echo $station_names|tr ',' '\n')|expand -t 16
  exit 0
fi

echo $@|grep 'NHK-\(R1\|R2\|FM\)$' && ./rec_radiru.sh $@
echo $@|grep 'NHK-\(R1\|R2\|FM\)$' || ./rec_radiko.sh $@
  • ラジオ局の一覧は-lオプションを指定するのだ。
$ rec_radikoru.sh -l
---- ID ----    ---- NAME ----
NHK-R1          NHK-R1
NHK-R2          NHK-R2
NHK-FM          NHK-FM
TBS             TBSラジオ
QRR             文化放送
LFR             ニッポン放送
RN1             ラジオNIKKEI第1 
RN2             ラジオNIKKEI第2
INT             InterFM
FMT             TOKYO FM
FMJ             J-WAVE
JORF            ラジオ日本 
BAYFM78         bayfm78
NACK5           NACK5
YFM             FMヨコハマ 
HOUSOU-DAIGAKU  放送大学

ダウンロードとインストール

開発&利用環境
  • OSX10.8.2
  • bash -version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
Copyright (C) 2007 Free Software Foundation, Inc.
timerコマンド
  • timer0.1.zipをダウンロードする。
    • 解凍したら、install.shを実行する。
    • デフォルトは、/usr/local/binにインストールされる。
    • 引数でパスを指定すると、指定したディレクトリにインストールされる。
radikoらじる★らじるを録音する場合
  • Xcodeをインストールして、Preferences... >> Downloads >> Components >> Command Line Tools をインストールしておく。
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew install wget
$ brew install swftools
$ brew install rtmpdump
$ brew install ffmpeg
  • さらに、rtmpdump2.4(バイナリ版)もダウンロードして、インストール。

使い方

以下、すべてのスクリプトを/usr/local/binにインストールして、/usr/local/binがコマンドサーチパスになっているbash環境。

  • 2件の予約を追加する。
    • 2/10の17時から、NHK-FMを1時間録音。
    • 毎週日曜の17時から、J-WAVEを1時間録音する
$ timer -e 02/10 1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 NHK-FM
$ timer -e 0 1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ
  • 現在の予約リストを表示する。(-l=リストのL)
$ timer -l
======== launchd list
02/10 	1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 NHK-FM
0 	1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ
======== schedule list
Repeating power events:
  wakepoweron at 0:00AM every day
Scheduled power events:
 [0]  wake at 02/10/13 16:59:30
  • 対話モード(-i=インタラクティブのi)
    • 対象のタイマー予約を番号で指定して、
    • (c)コピーか、(d)削除を、選択する。
$ timer -i
     1:  02/10 	1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 NHK-FM
     2:  0 	1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ
Select Number :1
[c]opy  [d]elete :
  • 既存のタイマー予約を修正する場合は...
    • (c)コピーして修正し、
    • その後、古いタイマー予約を(d)削除するのだ。

timer -e 0 1700 /usr/local/bin/rec_radikoru.sh -o radikoru/ -t 3600 FMJ
  • 毎週日曜の17:00からJ-WAVEを1時間録音する。
timer -e 1-5 0515 open -a iTunes /Users/zari/Music/iTunes/iTunes\ Music/Unknown\ Artist/Unknown\ Album/J-WAVE_20130203-1701.m4a
  • 毎週月曜から金曜の朝5時15分に、iTunesで、録音したサウジ・サウダージを目覚まし代わりに再生する。
  • ボリューム:システムサウンドiTunesの設定に影響される、再生時間:停止するまで。
timer -e 1-5 0515 afplay -v 200 -t 300 /Users/zari/Music/iTunes/iTunes\ Music/Unknown\ Artist/Unknown\ Album/J-WAVE_20130203-1701.m4a
  • 毎週月曜から金曜の朝5時15分に、afplayコマンドで、録音したサウジ・サウダージを目覚まし代わりに再生する。
  • ボリューム:200、再生時間:300秒=5分。
timer -e 1-5 0515 osascript -e 'tell application "iTunes"' -e 'set sound volume to 100' -e 'play playlist "トップレート"' -e 'delay 60' -e 'stop' -e 'end tell'
  • 毎週月曜から金曜の朝5時15分に、AppleScriptiTunesのトップレートを選択して、目覚まし代わりに再生する。
  • ボリューム:iTunesボリューム=100(システムサウンドは未設定なので既存の設定に影響される)、再生時間:60秒
  • iTunesでシャッフルの設定をしておけば、毎朝違う音楽が聴ける。
timer -e 1-5 0515 osascript /Users/zari/Library/Scripts/timer_sample.scpt
  • さらに長いAppleScriptコードであれば、ファイルに保存しておき、それをtimer起動時に実行することもできる。
  • AppleScriptを実行できるということは、たいていのことはtimerコマンドから実行できるのだ。

*1:StationTVなどのTV視聴専用のアプリケーションなら、予約録画はスリープ解除と連動して実行される。それと同じことをTVの録画に限らず、一般的な処理すべてにおいて予約日時に実行できるようにしたいのだ。