2日おきにcron実行をする方法【奇数日・偶数日】

2020年2月13日Linux

結論

cronを下記のように指定すれば、2日に一度実行することができます。
※ただし閏年でなければ、年を越すことはできません

* * * * * [[ $( expr $( date +\%j ) \%2 ) = 0 ]] && /usr/local/bin/exec_per_two_days.sh

なぜ「2日毎」がこんなに複雑な記述の仕方になるのか?

cronでは何分おき、何時間毎、2か月に一度の書き方はそんなに難しくありません。

例えば2分に一度実行したい場合、「*/2」と記述すればその通りになります。

*/2 * * * * /usr/local/bin/test.sh

分における「*」は0-59を表します。「/2」は2ステップごとの意味ですので、「*/2」は「0,2,4,…58」を表します。

では、2日ごとも同様の書き方でいけるように思えますが、実は下記の記述だと最短1ヶ月でうまくいかなくなります。

* * */2 * * /usr/local/bin/test.sh

日における「*」は、「1-31」を表します。「*/2」はつまり、「1,3,5,…31」となるのですが、例えば「1/30, 1/31/, 2/1, 2/2」は「不実行日、実行日、実行日、不実行日」となってしまうため、2日に一度の実行が崩れてしまうのです。

したがって数ヶ月以上2日に一度の実行にしたい場合には、分や時間とは違った書き方をしなければなりません。

記述の解説

冒頭でも記述した通り、2日毎に実行するかの判断は以下のようにすればできますが、どうしてこうなるのかを一つ一つ解説していきます。

[[ $( expr $( date +\%j ) \%2 ) = 0 ]]

まずはdateコマンドで本日日付を取得しています。その時にjフォーマットを指定することで、1/1〜12/31を001-366までの通し番号で表現することができます。平年の場合は12/31が365となりますが、閏年に限っては12/31が366となります。

例えば2/5にdate +\%jを実行した場合、036が取得できます。

[[ $( expr 036 \%2 ) = 0 ]]

次にexprコマンドを実行することで、計算式の評価を行います。今回は「036%2」の計算を考えれば良いはずです。36は2で割り切れる、つまり余りの値は0なので、結果は0となります。

[[ 0 = 0 ]]

0 = 0 はもちろんtrueとなるため、2/2の実行は認められるということになります。

この記述の欠点と注意点

平年(閏年じゃない年)だと年越しで失敗する

冒頭でも書いた通り、この記述は平年では年を越すことができません。

なぜならdateをjフォーマットにしたとき、12/31は「365」となります。翌年の1/1は「001」となります。つまり、「実行日、実行日」と連続してしまうのです。(閏年の場合は「12/31」が不実行日のため、「不実行日、実行日」の流れが崩れずにすみます。)

もし年単位で2日おき実行をしたい場合は、cronの記述で頑張るよりは実行スクリプト内で工夫した方がおすすめです。

奇数日・偶数日が直感的ではない

当然ですが、jフォーマットの通し番号の奇数・偶数と日付の奇数・偶数が一致するとは限らないので、その点は注意した方がいいです。

例えば2/5は日付的には奇数日ですが、jフォーマットにすると036と偶数となります。

イメージに引っ張られて、意図しない実行タイミングになってしまうこともあるので、くれぐれも注意してください。

サイト運営者 えぬたけ


都内で働くゆるふわフルスタックwebエンジニア。

Linux

Posted by enutake