UNIX 系システムには cron(d) と呼ばれるジョブの実行デーモンが存在しています。簡単にいえば「指定した日時や時刻に特定のコマンドを実行させる仕組み」です。「毎日午前1時にデータベースのバックアップコマンドを実行する」とか、「1時間おきに特定のスクリプトを実行する」とか、そういうものです。バックアップや集計目的のジョブを自動実行させようとすると cron を使うことになると思います。

例えばですが、毎日午前0時になったら /usr/local/bin/xxx.sh を実行したい、という場合は以下の1行を /etc/crontab に加えます:
0 0 * * * /usr/local/bin/xxx.sh

最初の5要素は実行タイミングです。1つ目が分、2つ目は時、3つ目は日、4つ目は月、5つ目が曜日を指定します。上記例では分に0、時に0が指定されているので「毎日午前零時」を指定していることになります。そして6番目の要素が実行させるジョブです。 というわけで上記の1行は毎日午前0時になったら /usr/local/bin/xxx.sh を実行する、という指定をしていることになるわけです。この程度ならシンプルで分かりやすいですよね。


で、今回やりたかったことはこんな内容でした:
(1) 毎日午前零時になったら、
(2) あるディレクトリ(/usr/tmp/logs/)に移動して
(3) php backuplog.php YYYYMMDD 形式のジョブを実行する
(4) 上記の YYYYMMDD は前日を示す日付文字列

要は backuplog.php という、ログのバックアップを行うような PHP スクリプトが /usr/tmp/logs/ に用意されていて、それを自動実行したいのですが、パラメータとして前日の日付文字列を付けたい(前日分のログをバックアップしたいので、その日付をパラメータで渡す)ということです。例えば今日の日付が 20140314 だとしたら、20140313 という文字列を指定して実行したい、ということです。まあ、この要件自体はさほど珍しくはないかな、と。敢えて付けくわえると、ログファイルの場所の関係もあって、直接 /usr/tmp/logs/backuplog.php を実行するのではなく、最初に /usr/tmp/logs にカレントディレクトリを移してから実行したい、という点が要件にあります。まあ、でも、これも珍しい要件とは言えないのかな・・・

ただ実現できるまで結構ハマりました。いくつか隠れた落とし穴があるんです。この要件を実現する crontab の記述内容をすぐ&正確に言える人ってどのくらいいるんだろ・・・



要件のうち、問題になりそうなところを1つずつ解決していきましょう。まず毎日午前零時になったら、は上記で説明しているので問題ないです。

次にカレントディレクトリを移動してからコマンドを実行する、という点。端末上だと cd /usr/tmp/logs と php backuplog.php YYYYMMDD という2つのコマンドを実行することになるのですが、それをどのように1行で記述するか、という問題です。 ただ、これも実は答はシンプルで単に「;(セミコロン)で区切って記述するだけ」です(端末上でもこの書き方で連続実行ができます)。なので crontab の6つ目の要素は cd /usr/tmp/logs;php backuplog.php YYYYMMDD という書き方になります。

さて、ここからがちょっと難しくなります。前日を示す文字列パラメータをどうやって作成すればよいでしょう? これは date コマンドを使うことになります。UNIX の date コマンドはそのまま実行すると日付時刻を表示するのですが、パラメータによって対象日やフォーマットを変更することができます。まず普通に実行するとこんな感じに表示されます:
# date
2014年  3月 14日 金曜日 04:53:43 UTC

このフォーマットを YYYYMMDD 形式に変えるには + オプションで形式を指定します:
# date +%Y%m%d
20140314

いい感じですね、でもこれは今日の日付です。では「前日」をどのようにすればいいでしょうか? こちらは --date オプションで指定することができるようです:
# date --date '1 day ago' +%Y%m%d
20140313

目的の文字列が取得できました! 後はこの結果をコマンドのパラメータとして渡せればいいので、上記コマンドを `(バッククォート)で括ってこんな感じにすると、目的のコマンドが完成したことになります。試しにコマンドラインから実行すると期待通りの結果が得られます:
# php backuplog.php `date --date '1 day ago' +%Y%m%d`


ということは、全てあわせて crontab に追加する内容はこれでいい、ということに・・・
 0 0 * * * cd /usr/tmp/logs;php backuplog.php `date --date '1 day ago' +%Y%m%d`

・・・実はこれ、(惜しいけど)まだ間違ってます。crontab では % という文字にはエスケープが必要になるのでした。最終的な正解はこちらです:
 0 0 * * * cd /usr/tmp/logs;php backuplog.php `date --date '1 day ago' +"\%Y\%m\%d"`

スケジュールエージェントはデバッグが難しい・・・