crontabの設定メモ

crontabの設定は簡単なのだが、ちゃんと理解しようとすると意外と多くの関連する知識が必要なのであった...。以下、crontabの設定しながら覚えたことメモ。

環境

  • MacBook OSX 10.5.6
  • zariユーザーでログイン中
  • ターミナルでbashを利用

コマンド書式

crontab -l
# zariユーザーのcron設定を表示する

crontab -e
# zariユーザーのcron設定をviを起動して編集する

sudo crontab -u Guest -l
# ルート権限で認証して、Guestユーザーのcron設定を表示する
  • 実行する時間の設定とコマンドから構成される。
* * * * * command
| | | | |
| | | | `--曜日(0:日 1:月 2:火 3:水 4:木 5:金 6:土 7:日)
| | | `----月
| | `------日
| `--------時
`----------分

例:
* * * * * command #1分ごとに実行する
*/2 * * * * command #2分ごとに実行する
2 * * * * command #毎時2分に実行する(分の単位が2の場合実行)
10,50 * * * * command #毎時10分と50分に実行する(分の単位が10と50の場合実行)
2 0-6 * * * command #毎日0時から6時は、2分に実行する(分の単位が2、かつ、時の単位が0から6の場合実行)

以下、関連する知識...

標準入力・標準出力・標準エラー出力

  • ターミナルでコマンドを実行すると、コマンドによっては何らかの情報を返してくる。
    • 例えば、lsであれば指定されたディレクトリに含まれるファイルやディレクトリの一覧情報を返す。(標準出力)
    • cdなんかは、指定されたディレクトリに移動するだけで、特に何も返さない。
    • また、sl*1なんて間違ったコマンドを入力すると、普通は-bash: sl: command not foundなんてエラー表示される。(標準エラー出力
  • 特に指定しない限り、上記のように返された文字情報は、ターミナルのウィンドウに表示されてコマンド利用者に何かを伝えてくれる。
  • シェル内部では、これらの情報を標準出力、標準エラー出力という概念で取り扱っているらしい。
    • 何も指定がない場合、標準出力、標準エラー出力とも端末(画面)に表示されるようになっている。
    • もし、以下のように指定すれば、出力先にファイルが指定されたことになり、ファイル名:my_textとして保存される。
$ ls >my_text*2
$ cat my_text
Desktop
Documents
Downloads
Library
...
    • ちなみに、標準入力という概念もあって、これは通常キーボードになっている。
$ read DATA
123 #123とキー入力した
$ echo $DATA
123
$ read DATA <in_text #ファイルin_textには"ABC"が保存されている
$ echo $DATA
ABC
      • read DATA 後の 123 が標準入力のキーボードから入力され、変数DATA に格納される。
      • lsの引数も標準入力をファイルに切り替えて設定できそうな気になるが、
$ ls <in_text
      • lsコマンドは引数がない場合もそのまま実行して、単に引数なしのlsが実行された結果が返ることになる。
      • 標準入力のリダイレクトが利用できるのは、標準入力を引数として利用できるコマンドの場合のみ。(read、cat、more、less、sort...等)
    • 3つの標準には、ファイルディスクプリタ番号が割り当てられている。(標準入力:0 標準出力:1 標準エラー出力:2)
    • file、 >>fileという書き方にはファイルディスクリプタ番号が省略されていて、明示的には 0file、 1>>file となる。(0と1は省略可能)
    • よって、標準エラー出力を指定するには、明示的にそのファイルディスクプリタ番号の2を付け加えれば良い。
$ sl 2>in_text
$ cat in_text
-bash: abc: command not found
    • 標準出力、標準エラー出力の両方ともファイルに保存する場合は、>& も利用できる。
$ echo sample_text >abc
$ cat abc xyz >&my_text
$ cat my_text
sample_text
cat: xyz: No such file or directory

>file_name 標準出力をfile_nameに上書き
2>>file_name 標準エラー出力をfile_nameの末尾に追記
>&file_name 標準出力と標準エラー出力をfile_nameに上書き
>&num 標準出力をファイルディスクリプタ番号numと同じファイルやデバイスに出力する
num1>&num2 ファイルディスクリプタ番号num1をファイルディスクリプタ番号num2と同じファイルやデバイスに出力する
>/dev/null 2>&1 標準出力と標準エラー出力を破棄する
(標準出力が/dev/nullにセットされ実行、標準エラー出力は標準出力(左記より/dev/null)にセットされ実行)

ファイルディスクリプタ

  • OSがファイルやデバイス(キーボード、画面等)の外部にアクセスする時に、それを識別するために利用する情報。
  • ファイルディスクリプタは整数の番号で管理されていて、0標準入力(キーボード)・1標準出力(画面)・2標準エラー出力(画面)は最初からシェルが利用している。
  • ユーザーが新たにファイルを一つ開くと、そのファイルにはファイルディスクリプタが割り当てられて識別、管理されることになる。

mail

  • cronで予約設定したコマンドを実行する時にも、様々な情報が返される可能性がある。
  • しかし、標準出力や標準エラー出力が画面のままでは、返される情報を確認することが出来ない。
  • そこで、cronは標準出力や標準エラー出力が発生した場合、その情報を各ログインユーザー宛のメールで送信してくれる。
  • そのメールはmailコマンドで確認できる。
  • mailコマンド実行中の指示コマンド(mailコマンド実行後、?プロンプトが表示されていればメールを閲覧するモード)
指示コマンド 機能
? コマンド一覧表示
[メール番号] [メール番号]のメールを表示する(既読になる)
v 選択カーソル > が示すメールを、エディタ(例:vi)で表示する(既読になる)
v[メール番号] [メール番号]のメールを、エディタ(例:vi)で表示する(既読になる)
f[メール番号] 行先頭の選択カーソル > を、[メール番号]に移動する(未読のまま)
h 現在の選択カーソル > の位置を含むメールリスト表示する
s 選択カーソル > が示すメールを、~/mboxの末尾へ保存する
s[ファイル名] 選択カーソル > が示すメールを、[ファイル名]の末尾へ保存する
s[メール番号] [ファイル名] [メール番号]のメールを[ファイル名]の末尾へ保存する
d 選択カーソル > が示すメールを削除する
d[メール番号] [メール番号]のメールを削除する
q 現在の状態を保存して終了。(既読メールはmboxへ、削除メールは削除される)
x 現在の状態を破棄して終了。(mailコマンド実行前の状態に戻る。)
  • メールリストが1ページ以上ある場合、fでカーソル位置を指定して、hでその周辺をリスト表示することで、目指す位置を表示することができる。
  • mailコマンドで一覧表示されるメールは、私書箱(/var/mail/USER_NAME)に届いている未読メールと考えることができる。
  • メールが既読になると、私書箱(/var/mail/USER_NAME)から自分の手元(~/mbox)に移動される。
  • メールの保存先は、~/mbox以外にも自由に指定できる。(sコマンド)
$ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/zari": 52 messages 30 new 52 unread
 N  1 zari@zari-MacBook.lo  Sat Feb 28 07:48  20/852   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  2 zari@zari-MacBook.lo  Mon Mar  2 09:41  20/852   "Cron  diff -q ~/Library/StickiesDatabase ..."
>N  3 zari@zari-MacBook.lo  Mon Mar  2 09:56  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  4 zari@zari-MacBook.lo  Mon Mar  2 10:53  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  5 zari@zari-MacBook.lo  Mon Mar  2 11:01  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  6 zari@zari-MacBook.lo  Mon Mar  2 11:03  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  7 zari@zari-MacBook.lo  Mon Mar  2 11:08  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  8 zari@zari-MacBook.lo  Mon Mar  2 11:10  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N  9 zari@zari-MacBook.lo  Mon Mar  2 11:14  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 10 zari@zari-MacBook.lo  Mon Mar  2 11:18  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 11 zari@zari-MacBook.lo  Mon Mar  2 11:20  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 12 zari@zari-MacBook.lo  Mon Mar  2 11:25  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 13 zari@zari-MacBook.lo  Mon Mar  2 11:29  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 14 zari@zari-MacBook.lo  Mon Mar  2 11:31  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 15 zari@zari-MacBook.lo  Mon Mar  2 11:38  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
 N 16 zari@zari-MacBook.lo  Mon Mar  2 11:42  19/842   "Cron  diff -q ~/Library/StickiesDatabase ..."
? ?
    Mail   Commands
t 		type messages
n				goto and type next message
e 		edit messages
f 		give head lines of messages
d 		delete messages
s  file		append messages to file
u 		undelete messages
R 		reply to message senders
r 		reply to message senders and all recipients
pre 		make messages go back to /var/mail
m 			mail to specific users
q				quit, saving unresolved messages in mbox
x				quit, do not remove system mailbox
h				print out active message headers
!				shell escape
cd [directory]			chdir to directory or home if none given

A  consists of integers, ranges of same, or user names separated
by spaces.  If omitted, Mail uses the last message typed.

A  consists of user names or aliases separated by spaces.
Aliases are defined in .mailrc in your home directory.

? 1
Message 1:
From zari@zari-MacBook.local  Sat Feb 28 07:48:03 2009
X-Original-To: zari
Delivered-To: zari@zari-MacBook.local
From: zari@zari-MacBook.local (Cron Daemon)
To: zari@zari-MacBook.local
Subject: Cron  diff -q ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa || cp ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa
X-Cron-Env: 
X-Cron-Env: 
X-Cron-Env: 
X-Cron-Env: 
X-Cron-Env: 
Date: Sat, 28 Feb 2009 07:48:02 +0900 (JST)

Files /Users/zari/Library/StickiesDatabase and /Users/zari/Documents/StickiesData.aaa differ
  • mail -fオプションで、ファイル~/mboxに移動されたメールを閲覧するモードになる。
  • mail -f FILE_NAMEオプションで、FILE_NAMEに保存されたメールを閲覧するモードになる。
  • mail -u USER_NAMEオプションで指定したユーザーのメールも確認できる。(管理者権限必要)
$ sudo mail -u Guest

不要なmail

  • mailはコマンドが返す情報を伝えてくれる便利な仕組みなのだが、時には悩みの種になることがある。
  • 例えば、Spotlight検索を実現するために1分毎に実行するStickiesDatabaseのコピー処理なんかは、放っておくとあっという間にmailのサイズが馬鹿でかくなる。
  • メールの内容は「StickiesDatabaseが見つからない」なのだが、そんなことは重要ではなく、不要なのだ。(コピーできる時にコピーしてくれるだけで十分)
  • そんな時は、標準出力や標準エラー出力を破棄してしまう方法がある。
  • リダイレクト指定の >/dev/null 2>&1 をコマンドごとに付加すればOK。
* * * * * diff -q ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa >/dev/null 2>&1 || cp ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa >/dev/null 2>&1
  • または、cronの環境変数 MAILTO="" を指定する。(定期的に実行するコマンドよりも上で(先に)指定しておく)
  • 標準出力・標準エラー出力は発生するが、メールの送信先が""なので、どこにも送信されないことになる。
  • MAILTO="" を指定した後の、すべてのコマンドでメールは送信されない。
MAILTO=""
* * * * * diff -q ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa || cp ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa

test

  • 利用例:~/Library/StickiesDatabaseが存在すれば、コピーする。
* * * * * test -e ~/Library/StickiesDatabase && cp ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa
  • 数値・文字列・ファイルを比較する時に利用される。様々な比較オプションがある。
オプション 予想される単語 意味
-e exist test -e file ファイルが存在すれば真
-s size test -s file ファイルが0より大きいサイズなら真
-f file test -f file ファイルであれば真
-d directory test -d file ファイルがディレクトリであれば真
-r read test -r file ファイルの権限が読み取り可能であれば真
-w write test -w file ファイルの権限が書き込み可能であれば真
-x execute test -x file ファイルの権限が実行可能であれば真
-nt newer than test file1 -nt file2 ファイル1の方が新しければ真(ファイル2と比較して)
-ot older than test file1 -ot file2 ファイル1の方が古ければ真(ファイル2と比較して)
-z zero test -z str 文字列のサイズが0ならば真
-n not zero test -n str 文字列のサイズが0より大きければ真
= test str1 = str2 文字列1と文字列2が等しければ真
!= test str1 != str2 文字列1と文字列2が等しくなければ真
-eq equal test num1 -eq num2 num1=num2なら真
-ne not equal test num1 -ne num2 num1≠num2なら真
-lt less than test num1 -lt num2 num1 < num2なら真
-le less than or equal test num1 -le num2 num1 ≦ num2なら真
-gt greater than test num1 -gt num2 num1 > num2なら真
-ge greater than or equal test num1 -ge num2 num1 ≧ num2なら真
  • testコマンドは[]を利用して以下のように書くことも出来る。(上下のペアは同義)
test num1 -eq num2
[ num1 -eq num2 ]

test -e ~/Library/StickiesDatabase
[ -e ~/Library/StickiesDatabase ]
  • [はコマンドなので、[の後と、]の前に必ずスペースを入れる必要あり。

複数コマンドの実行

コマンド1 ; コマンド2
コマンド1を実行、終了したら(エラーで終了しても)、コマンド2を実行
cd ~/Desktop;ls -l
コマンド1 && コマンド2
コマンド1を実行、結果が真であれば、コマンド2を実行
test -e ~/Library/StickiesDatabase && cp ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa
  • ~/Library/StickiesDatabaseが存在していたら、コピーする。
コマンド1 || コマンド2
コマンド1を実行、結果が偽であれば、コマンド2を実行
test -e ~/Documents/StickiesData.aaa || ln ~/Library/StickiesDatabase ~/Documents/StickiesData.aaa
  • ~/Documents/StickiesData.aaaが存在していなければ、ハードリンクを作成する。
コマンド1 | コマンド2
コマンド1の標準出力を、コマンド2の標準入力にして実行
ls | wc -l
  • lsを実行して、出力される結果に何行あるか数える。(ファイルやディレクトリの数を確認できる。)

run-parts

  • run-partsコマンドは、指定されたディレクトリに存在するコマンドやシェルスクリプトを順番にすべて実行してくれる。
  • MacBook OSX 10.5環境には、run-partsコマンドはプリインストールされていなかった。


periodic

  • run_partsコマンドに替わって、osxではperiodicコマンドが利用できるのであった。

*1:実は自分の場合、蒸気機関車のSLが走る!http://d.hatena.ne.jp/zariganitosh/20080702/1214953436

*2:>my_text lsでもOK。>my_textはlsの引数ではない。標準出力を指定するコマンドのようなもの。