【目覚まし】cron + シェルスクリプトで任意の音声ファイルを再生・停止する

iTunes で毎朝オーディオを再生しようとするとどうも面倒(何やらツールを使う必要がある)なので、cron から普通にシェルで実行できないものかとスクリプトを書いてみました。

要件としては、以下のような感じです。

  • cron で実行できるよう、普通にシェルのコマンドで実装する
  • 再生の停止を、Ctrl-C はともかく、コマンドラインからもできるようにする
  • 停止の際には、ちゃんとサブコマンド一式も停止すること
  • pid ファイルはゴミになりがちだから使いたくない

使い方としては、alarm start でフォアグラウンドで再生を開始、Ctrl-C、もしくは alarm stop で再生を停止します。

以下は Mac OS X 用ですので afplay(1) コマンドを使っていますが、そこと、オーディオファイルの拡張子(以下では *.m4a になっている)さえ変えれば、*NIX 系のシステムならば動くと思います。

子プロセスと Ctrl-C の関係とかは、右記なんぞを参照してください。 ∥ Ctrl+Cとkill -SIGINTの違いからLinuxプロセスグループを理解する | ギークを目指して

#!/bin/bash

## このディレクトリ下のファイルを連続再生する
path="/Users/knaka/Dropbox/iTunes/岡崎律子/for RITZ/"

## 適当に、ユニークな名前。これをターゲットに kill をする
process_name="AlarmProcessHogeFuga"

## ID で指定したプロセスの子孫プロセス ID 全てを再帰的に返す
function get_children() {
	for pid in $(pgrep -P $1)
	do
		echo $pid
		get_children $pid
	done
}

subcmd=$1

case $subcmd in
	## 再生を開始する
	start )
		## シンボリックリンクを解決し、シェルスクリプトのフルパスを求める
		self="$(p="$0";while true;do t="$(readlink "$p")";cd "$(dirname "$p")";test -z "$t"&&break||p="$t";done;echo "$(pwd -P)/$(basename "$p")")"
		## Bash のプロセスは、exec しなおさないと名前を変えられない
		exec -a "$process_name" bash "$self" _play
		;;
	## 再生を停止する
	stop )
		pid=$(pgrep -f "$process_name")
		kill $pid $(get_children $pid)
		;;
	## exec しなおした際に、実際に再生を開始する
	_play )
		for i in "$path"/*.m4a # 拡張子はお好みで
		do
			## 単純な再生コマンド。これは OS X の場合
			afplay "$i"
		done
		;;
esac

子プロセスの停止は、シグナルハンドラで行なった方が良かったかな…。まあいいか。

もっと簡単な実装方法がありますよー、という方がいらっしゃったら教えていただけるとありがたし。

では。