それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

いまさらcrontabのメリットと書き方を見直す

crontab って便利だけどわかりにくさがある

crontab の書き方がおかしくてうまく実行されないとか、そんな書き方があったのか!となることがある。

なので、systemd を使って定期実行することが多いんだけど、そうすると今度は管理が大変になる。

なんだこの定期実行は!となってしまう。

crontab で敢えて書くことの利点

「なんで、いまらさ cron なんだよ。このオッサンは!!」と思われるかもしれない。でもcronを使うメリットは少なからずあると思います。

メリット

cronで書くと次のようなメリットがあると思います。

  • 一覧ですぐ確認できる
  • /etc/cron.d に絶対ある
  • コマンドの書き方がシンプル
  • メール通知がかならずある。
  • 書式はパターン化される。

最初に cron は「クロン」と呼びます。

ときどき、「くぅーろん」とか「くろーん」とか余計な母音や長音を含めている人が多いです。くぅーろんだと coulomb、くろーんだとcloneになってしまいます。カタカナで表記するならクロンがベターだと思います。

目次

メリット1:cron は一箇所に集まります。

cron を編集するには crontab -e コマンドで行います。

crontab -e 

ユーザーごとにcrontab -e を起動してcron を定義することができます。

crontab -e で登録内容を一覧できる。

crontab -e を見ておけば、どんなコマンドを登録したか一目瞭然ですね。

しかも、編集結果はファイルとして保存されています。

ユーザーのcronの場所

ユーザーが作った cron 定義は次の場所に集まります。

/var/spool/cron/crontabs

一般ユーザーtakuya の場合なら次になります。

/var/spool/cron/crontabs/takuya 

root ユーザーのcron (つまり sudo crontab -e ) のファイルは次に保管されています

/var/spool/cron/crontabs/root

システムのcron の場所

root ユーザーでもなく 一般ユーザーでもない cron のファイルがあります。それはシステムに登録したcrontab です。

おもに、.deb や .rpm などのパッケージを入れたときに作られます。代表的なものを挙げると logrotate と php です。logrotate はその名の通り。php は セッションキャッシュを破棄しています。

/etc/cron*

パッケージのファイルは /etc/ に集められています。

ココさえ見ておけばシステムに登録された定期実行をすべて知ることができる。

/etc にあるcron の例

root@sakura:~# tree  /etc/cron*
/etc/cron.d
├── php
└── sysstat
/etc/cron.daily
├── bsdmainutils
├── dpkg
├── logrotate
├── mlocate
├── ntp
└── sysstat
/etc/cron.hourly
/etc/cron.monthly
└── 0anacron
/etc/crontab 
/etc/cron.weekly
├── 0anacron
├── fstrim
└── man-db

システムのcron のファイルは /etc/cron* に集められています。

逆に言えば、crontab は ユーザーとして登録するだけではなく、自分で自由にファイル単位に分割して管理することができるのです。

cronファイルの各行を用途別に分割すれば、バージョン管理と合わせ技ができてとても便利だと言えます。

システムの cron.d へ登録

前述したファイルの構造を知っていれば、システムにcronを追加したいとき、次のように書くことができまsy.

sudo  sh -c "echo 0 * * * * command_a > /etc/cron.d/mycron" 
sudo  sh -c "echo 0 * * * * command_b >> /etc/cron.d/mycron" 

cronに登録するコマンドを用意できます。 こうすれば、「手作業でsudo crontab -e から編集してください」などと、ミスが起きやすい糞マニュアルを書かず済みますね。

/etc/cron.weekly /etc/cron.daily /etc/cron.hourlyについて

ここには定期実行したい「スクリプト」を設置します。

実行フラグ(+x)が付与されたスクリプトを設置すれば実行されます。

メリット2:コマンドの記述がシンプル

たとえば、毎日1回 0:00 に コマンドを実行したいのであれば、次のように書くことができます。

@daily /path/to/my_command > /dev/null

楽ですね。<時間・コマンド>を記述するだけです。シンプルです。コマンドだけです。

もし複雑なコマンドを実行したいのであれば、コマンドをファイルに記述してchmod +x するだけです。

コマンドに書くのではなく、ファイルに独立させると管理とテストが容易になるので楽ですね。

cronはSHELL=sh で実行される。

基本的に cron は sh -c で実行されます。なので先程のコマンドの記述例であればつぎのように実行される。

sh -c '/path/to/my_command > /dev/null ' 

sh として実行されると思っておけば大丈夫です。

sh → bash に変える

もし、bashで cron を実行したいのであれば、次のように書くことができます。

SHELL=bash
@daily /path/to/my_command > /dev/null

bash に変えるメリット

bash に変えておけば、いくつかの bash らしい機能が使えます。

次のように、複数のコマンドを必ず2回実行する

@daily { /path/to/my_command ; /path/to/my_command ;  }> /dev/null

条件分岐

条件分岐はシェルスクリプトそのままなので if を使わずに && でやるのがシンプル

@daily assert_that && do_this 

前半で条件判定を書きます。コマンドが正常終了すると次が実行される。コマンドの正常終了(return 0)を以て次のコマンドに制御が移ります。

これも、bashでcrontを書いていれば少し複雑なコマンドを書けますね

@daily ( echo 1 && aaaaaaa ) && echo ok

シェルなので、 if 文も書けるでしょう。。があまりやらないよね。

@daily if echo 1 ; then echo 2 ; fi

シェルなのでシンプル

シェルなので、シンプルに書くことができる。crontab の書き方がわからないという場合は、cronよりも、たぶんシェルの書き方がわからないだけだと思います。

メリット3:メール通知

cron はその実行結果がメールで飛ばされます。しかし受け取ってない人がほとんどなのではないでしょうか。

受取先を指定しましょう。

MAILTO=takuya@example.com
@monthly  /home/takuya/repos/report  -t au_wallet 

上記のようにメール送信先を指定すると、実行結果がメアドに送信されてきます。

この送信はsendmail コマンドで行われるので、システムのsmtp を適切に設定しておく必要があります。

エラーだけほしい→シェル機能

エラーだけメールが欲しい場合は、リダイレクトするだけで解決

MAILTO=takuya@example.com
@monthly  /home/takuya/repos/report  -t au_wallet > /dev/null

これもシェルですね。

シェルの知識はLinuxでの基礎知識ですから、自信がないなら覚えましょう。

ChatOpsしたい

たとえば、slack に通知したいときはメールをトリガーにSlack投稿するフックを仕込んでおけばいいでしょう。メールをトリガーに実行するサービスはやまほどあります。

もちろんLinux内部で完結させることも可能です。

Linux でメールのローカル配送(外部に送らないで内部→内部ユーザへ送信)した後にフックを起動することももちろんできます。旧来からの メールの.forward 書くだけです。

メリット4:書式はパターン化してある。

この記事で書いていた @montly という時刻の指定方法はあまり出てこないのですが、覚えやすいので覚えておきましょう。

@daily まず最初に覚えておけばあまり困らないと思います。

書式は難しくない。

cron の書式が面倒くさいと感じる人もいるでしょう、逆です。書式があるからわかりやすいのです。

最初の 5 つのフィールドの代わりに、 8 つの特殊な文字列のいずれか 1 つを指定してもかまわない:

              文字列         意味
              ------         ----
              @reboot        起動時に一度だけ実行。
              @yearly        年に一度だけ実行 ("0 0 1 1 *" と等価)。
              @annually      (@yearly と同様)
              @monthly       月に一度だけ実行 ("0 0 1 * *" と等価)。
              @weekly        週に一度だけ実行 ("0 0 * * 0" と等価)。
              @daily         一日に一度だけ実行 ("0 0 * * *" と等価)。
              @midnight      (@daily と同様)
              @hourly        一時間に一度だけ実行 ("0 * * * *" と等価)。

ね?簡単でしょ?

日付時間を指定するには

crontab の書式は左から順番につぎのようになっています。

m h dom mon dow
0 0  10 * * path/to/command

これは単に英語で書いてあるだけです。

m: minute
h: hour
dom: day of month
mon: month
dow: day of week

覚えにくいときはこのように見てください

 ┌────────── minute (0 - 59)
 │ ┌──────── hour (0 - 23)
 │ │ ┌────── day of month (1 - 31)
 │ │ │ ┌──── month (1 - 12)
 │ │ │ │ ┌── day of week (0 - 6 => Sunday - Saturday, or
 │ │ │ │ │                1 - 7 => Monday - Sunday)
 ↓ ↓ ↓ ↓ ↓
 * * * * * command to be executed

ね?簡単でしょ?

書式を 日本語に訳するとこうです。

英語 日本語 指定可能な値
m 0-59
h 0-23
dom 月内日 1-31
mon 1-12 (もしくは名前。下記を参照
dow 曜日 0-7 (0 と 7 は日曜日。もしくは名前)

具体例を覚えておけば大丈夫です

ぐだぐだ書きましたが、書式が決まってるのだからテンプレです。

1 時間おきに実行したい

m h dow mon dow
0  *  *    *   *  command

これで毎時0分に実行されます。

毎日1回実行したい

m h dow mon dow
0  0  *    *   *  command

これで毎日0時0分に実行されます。

毎土曜1回実行したい

m h dow mon dow
0  0 *    *   SAT  command

これで毎週土曜日0時0分に実行されます。

曜日の指定はちょっと特殊です。

数字 英語名 日本語
0 SUN
1 MON
2 TUE
3 WED
4 THU
5 FRI
6 SAT
7 SUN 日(*2回目 Linuxだけ)

GNU/Linuxだけ0と7の両方が日曜日です. xBSDなどのUNIXでは、7(sun)は存在しないようです。

Linuxは0から数えるプログラマのほかに、1から数えるプログラマが存在することに対応しているんんでしょうね。優しいね。

まぁ0=SUN だと覚えておけばいいです。

でも、面倒くさいのでアルファベットで書くほうが楽です。chmod 755 より chmod u=rwx,g=rx,o=rx  がわかりやすいのと同じことです。

曜日は英語名も数字もOK

曜日は英語で指定しても数字でもオッケらしいです。

###次の例は、全て「毎週日曜日に実行」です。
0  0 *    *   SUN  
0  0 *    *   sun
0  0 *    *   0
0  0 *    *   7

ここだけは表記が揺れるので困りますね。

その他サンプル

といっても覚えにくいのはあるのでテンプレを書いていきましょう。

m h dow mon dow
### 毎週日曜日に実行
0  0 *    *   SUN  
### 3時間おきに実行
*/3 * * * * 
#### 毎月25 日の0:00に実行
0 0 25 * * 
#### 毎月5日の毎時に実行
0 * 5 * * * 
### 土日以外
0 0 * * 1-5
### 月金だけ
0 0 * * 1,5
### 奇数時間 1,3,5,7,9 に実行
0 1-9/2 * * * 

それでも覚えられない?

ちなみに、これはman を読めば書いてあります。 man 5 です。

man 5 crontab

こまってググってもネットには、この記事のようなポエムしかありません。まずman をみましょう。

cron がわからないのではなく、man の読み方を知らないのでありませんか。

man見ても、なぜか man crontab(1) を見てしまっていませんか?

takuya@:~$ man -k cron
cron (8)             - 予定されたコマンドを実行するデーモン(Vixie Cron)
crontab (1)          - 各ユーザーのための crontab ファイルを管理する (V3)
crontab (5)          - cron を駆動するための一覧表
dh_installcron (1)   - cron スクリプトを etc/cron.* へインストールする
anacrontab (5)       - configuration file for anacron
anacron (8)          - runs commands periodically

man -k cron で cron の man を探すと crontab(1) と crontab (5)があります。crontab(5)を見ると詳しく書いてあります。

書き方に迷ったらまずman を見ることをオススメします。

まとめ

cron について見直してみました。

cronのメリットは

  • ファイル単位
  • シェルコマンド書くだけ
  • 場所が決まってる
  • 書式が決まってる

の点に良さを感じています。

これらはLinux基礎知識だけで理解できます。基礎知識をちゃんと知っていれば問題なく扱えると思います。また /etc にファイルが集まりバージョン管理をしやすいところも良きところではないでしょうか。

systemd で書けると言われれば困るのですが、

おまけ

crontabのジョブが終わらないうちに、つぎのジョブが起動しちゃって困る?

そういうときは、taskspooler を使うと便利らしいですよ→ cron + α が欲しい時には ts(Task Spooler) のご利用をご検討下さいというメモ - ようへいの日々精進XP

わたしは、すでに実行中なら(pidファイルがあるなら)実行キャンセルにしてますけど。

関連資料

過去に書いた cron 関連の記事が散らかっているので、この記事にまとめて整理しました。よろしければ過去記事も見てやってください

そうだった、

うろ覚えなのですが「曜日と日付の同時設定はorで動いてしまう」みたいな制約ありませんでしたっけ(昔だけ?) - ngswのコメント / はてなブックマーク

間違えてましたすいません。ご指摘感謝です。

     注意:  コマンド実行の日は 2 つのフィールドで指定できる — 月内日および曜日である。 もし両方のフィールドが制限指
       定 (* 以外) であると、 いずれかのフィールドが現在時刻と合った時にコマンドが実行される。 例えば、
       ``30 4 1,15 * 5''
       とすると、毎月 1 日と 15 日および毎週金曜日の 午前 4:30 にコマンドが実行される。

おすすめのLinux書籍

Linuxの教科書とシェルコマンドの教科書が、いま一番でしょう。入門書として旬だと思います。ぜひ読んでみてください。

三宅さん、この本の著者とは知らず、ボドゲ会でハトクラやって遊んでたのです。ハトクラやりながらもっと訊いておけばよかったよ。素晴らしい書籍をありがとう御座います。

新しいLinuxの教科書

新しいLinuxの教科書

新しいシェルプログラミングの教科書

新しいシェルプログラミングの教科書