そのcron、本当に毎日動いてる?ハマったところと“祝日スキップ”

こんにちは、さるまりんです🐒

8月11日は山の日。山に登ったり、ちょっと一息ついたり――そんな祝日ですが、サーバー上のcronジョブたちは今日も変わらず動いています。

でも、ちょっと待ってください。「そのcronジョブ、祝日も必要ですか?」

今回は、cronでこれまでハマったことと、祝日には実行をスキップする簡単な方法を考えてみます。
関数として共通化できる形で、再利用して便利に使えるようにしていきますね。

⚠️ cronのありがちなハマりどころ

PATHが通ってない問題

cronで動かしたら、command not found で失敗していた…なんて経験ありませんか?

cronはログインシェルとは違って環境変数が限られているため、phpnodeなどのコマンドが見つからなくて使えないことがあります。

対策:フルパス指定 or PATHの明示

PATHをちゃんと書いておく

PATH=/usr/local/bin:/usr/bin:/bin

またはコマンドなどを絶対パスで指定します

/usr/bin/php /home/user/script.php

[[ ]]source が動かない?

cronはデフォルトで /bin/sh を使ってジョブを実行します(実際は環境によって異なりますが)。
そのため、[[ ]]source などbash専用の構文はエラーになります。

対策:crontab -eSHELL=/bin/bash を明示

SHELL=/bin/bash

この一行を crontab の先頭に書くだけで、bash構文が使えるようになります。

set -euo pipefail も忘れずに

過去の記事でも紹介したとおり、シェルスクリプトでは安全性の高い設定として次の1行を入れておきましょう:

set -euo pipefail

過去記事の「setとオプションを使って安全にシェルスクリプトを書こう」も参考にしてもらえると嬉しいです。

祝日はスキップしたい?

冒頭でも触れたように「祝日は実行しない」ようにするには、スクリプト内で判定して終了するのが簡単で安全と思います。
さらに、判定処理を関数化して共通化することで、複数のバッチ処理で再利用できて便利ですね。

祝日判定関数を共通化

ディレクトリ構成の例

/home/user/scripts/
├── common.sh ← 祝日判定関数など共通ロジック
├── holiday-aware.sh ← cronで呼び出す本体
└── holidays.txt ← 祝日リスト

common.sh(共通関数ファイル)

#!/bin/bash

HOLIDAYS_FILE="${HOLIDAYS_FILE:-$(dirname "$0")/holidays.txt}"

is_holiday_today() {
  local today
  today=$(date +%Y-%m-%d)
  grep -q "$today" "$HOLIDAYS_FILE"
}

holiday-aware.shcron用スクリプト本体)

#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/common.sh"

if is_holiday_today; then
  echo "今日は祝日なのでスキップします。"
  exit 0
fi

echo "通常処理を実行中..."
# ここに本来の処理を書く

holidays.txt の例

2025-01-01
2025-02-11
2025-08-11
2025-12-23

このファイルは祝日を yyyy-MM-dd 形式で持っています。

cronの設定例(crontab -e)

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin

0 5 * * * /home/user/scripts/holiday-aware.sh >> /var/log/batch.log 2>&1

これで毎朝5時にバッチを実行しつつ、祝日ならスキップできます!

holidays.txt を自動更新したい?

せっかくなので祝日も自動で管理したいですよね?

こんなワンライナーで、内閣府が提供している祝日CSVからholidays.txtを生成できます。

curl -s https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv \
  | iconv -f Shift_JIS -t UTF-8 \
  | tail -n +2 \
  | cut -d, -f1 \
  | awk -F'[/-]' '{printf("%04d-%02d-%02d\n", $1, $2, $3)}' \
  > ~/scripts/holidays.txt

やっていることは、祝日CSVから、ゼロパディング付き yyyy-MM-dd 形式で holidays.txt を生成です。
これを週1回などでcronに登録すれば、祝日リストも常に最新に保てます!

cronでハマった経験はこれまでも何度もあるので同じことで困らないようにここにメモして回避していきたいと思います。
そして、祝日はスキップというものにはお休みをとってもらうようにしてみます。

実際に平日だけでお休みの日ならスキップって処理結構あると思います。
お休みじゃなくて祝日の前後の営業日にずらすと言う場合もあるかな。それはまた改めて発展系を考えてみたいと思います。

cronにも“山の日”のようなお休みを。
サーバーさんに優しいプログラマーになれるかな。

読んでくださってありがとうございました。

それではまた!