こんにちは、かつコーチです。
set -eによるエラーハンドリングまで押さえたところで、ここからは実践・自動化編に入ります。
ここまで作ってきたシェルスクリプトは、手動で実行することを前提にしていました。
今回は、cronを使ってスクリプトを決まった時間に自動実行する方法を解説します。
cronとは?
目覚まし時計のように処理を予約する仕組み
cronとは、Linuxに標準搭載されているジョブスケジューラ(決まった時間に処理を自動実行してくれる仕組み)です。
イメージとしては、目覚まし時計や炊飯器の予約タイマーに近いです。
「毎朝7時に起きる」と時計にセットしておけば、あとは何もしなくても時間が来たら起こしてくれるのと同じように、cronに「毎日深夜2時にこのスクリプトを実行する」と登録しておけば、あとは自動でスクリプトが実行されます。
裏側ではcrond(cronデーモン)という常駐プロセスが1分おきに設定内容をチェックし、実行すべき時刻になったジョブを起動する、という仕組みで動いています。
なぜcronが必要なのか
手動実行には、次のような限界があります。
- 深夜や早朝の実行を、人が毎回起きて手動で行うのは現実的ではない
- 実行し忘れ・実行時刻のばらつきが発生する
- サーバー再起動などがあっても、人手を介さず継続的に動かしたい
ログのローテーション、データベースのバックアップ、レポートメールの送信など、「決まった時間に、決まった処理を、人手を介さず確実に行いたい」場面では、cronによる自動化が定番の解決策になります。
基本の書き方
crontab -eの使い方とcronの書式
cronにジョブを登録するには、crontab -eコマンドでそのユーザー専用のcron設定ファイルを編集します。
crontab -e
初回実行時は使用するエディタを聞かれるので、nanoか使い慣れたエディタを選びます。
cronの書式は、次の5つの項目とコマンドを1行にまとめて書きます。
分 時 日 月 曜日 コマンド
0 2 * * * /home/user/scripts/backup.sh
| 項目 | 指定範囲 | 意味 |
|---|---|---|
| 分 | 0〜59 | 何分に実行するか |
| 時 | 0〜23 | 何時に実行するか |
| 日 | 1〜31 | 毎月何日に実行するか |
| 月 | 1〜12 | 何月に実行するか |
| 曜日 | 0〜7(0と7は日曜) | 何曜日に実行するか |
| コマンド | – | 実行するスクリプトやコマンド |
*(アスタリスク)は「すべての値に一致する」という意味で、上の例の0 2 * * *は「日・月・曜日を問わず、毎日2時0分に実行する」という設定になります。
よく使う書式パターンをいくつか挙げておきます。
# 毎日深夜2時に実行
0 2 * * * /home/user/scripts/backup.sh
# 平日(月〜金)の9時に実行
0 9 * * 1-5 /home/user/scripts/report.sh
# 5分おきに実行
*/5 * * * * /home/user/scripts/healthcheck.sh
# 毎月1日の0時0分に実行
0 0 1 * * /home/user/scripts/monthly_summary.sh
*/5のようにスラッシュを使うと、「5の倍数のタイミングごと」という間隔指定ができます。
シェルスクリプトをcronに登録する手順
実際にスクリプトをcronに登録するまでの手順は、次の流れになります。
- スクリプトに実行権限を付ける(
chmod +x script.sh) - スクリプト内のパスをすべて絶対パスに統一する
crontab -eでジョブを登録するcrontab -lで登録内容を確認する
chmod +x /home/user/scripts/backup.sh
crontab -e
# 0 2 * * * /home/user/scripts/backup.sh >> /home/user/logs/backup.log 2>&1
末尾の>> /home/user/logs/backup.log 2>&1は、標準出力・標準エラー出力の両方をログファイルに追記する設定です。
これを付けておかないと、cron経由での実行結果がどこにも残らず、失敗しても気づけません。
よくあるつまずきポイント・エラー対処
PATHが通っていなくてコマンドが見つからないエラー
cron経由で実行される環境は、普段ターミナルで使っている対話シェルの環境変数を引き継ぎません。
これがcron初心者が最もつまずくポイントです。
❌ Before:ターミナルで動いたコマンドをそのままcronに登録する
0 2 * * * python3 /home/user/scripts/notify.py
ターミナルで直接実行すると問題なく動くのに、cron経由で実行すると失敗し、cronからのメール通知(あるいはログ)には次のようなエラーが記録されていました。
/bin/sh: 1: python3: not found
原因は、対話シェルでは~/.bashrcなどでPATHに/usr/binやpyenvのパスが追加されているのに対し、cronが実行時に使う環境変数は最小限のものしかない、という違いによるものです。
私も最初にこのエラーに遭遇したとき、「ターミナルでは動くのになぜ」と原因が分からず、1時間ほどcronの設定ミスを疑って調べ回りました。
✅ After:コマンドを絶対パスで指定するか、スクリプト冒頭でPATHを明示する
0 2 * * * /usr/bin/python3 /home/user/scripts/notify.py
または、スクリプトの冒頭でPATHを明示的に設定しておく方法もあります。
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
python3 /home/user/scripts/notify.py
which python3などで普段使っているコマンドの絶対パスを事前に確認し、cronに登録するコマンドはすべて絶対パスで書く、というのが最も確実な対処法です。
実行結果が分からず、失敗に気づけない
cronはデフォルトでは実行結果を画面に表示しません。
登録しただけで満足してしまうと、失敗していても誰も気づかない、という状態になりがちです。
対策として、前述のリダイレクト(>> ログファイル 2>&1)で必ずログを残すか、MAILTO変数を設定してcronの実行結果をメールで受け取る方法があります。
MAILTO="you@example.com"
0 2 * * * /home/user/scripts/backup.sh
小規模な個人運用であれば、まずはログファイルへの出力を徹底するだけでも、トラブルの早期発見につながります。
応用・一歩先の使い方
systemd timerとの違い
Ubuntu 26.04では、cronの代わりにsystemd timer(systemdが提供するスケジューリング機能)を使う選択肢もあります。
cronがシンプルな時刻指定に強い一方、systemd timerは「前回の実行が失敗したら次を遅らせる」「システム起動時に未実行分をまとめて実行する」といった、より高度な制御が可能です。
とはいえ、単純な定期実行であればcronのほうが設定がシンプルで、多くの現場でも今なお標準的に使われています。
まずはcronで基本を押さえ、より複雑な要件が出てきたタイミングでsystemd timerを検討する、という順序で問題ありません。
まとめ
この記事のポイント
- cronは「目覚まし時計」のように、決まった時刻にスクリプトを自動実行するジョブスケジューラ
crontab -eで「分 時 日 月 曜日 コマンド」の書式でジョブを登録する- cron経由の実行では対話シェルのPATHが引き継がれないため、コマンドは絶対パスで指定する
- ログ出力かMAILTOを設定し、実行結果を必ず確認できるようにしておく
次に読むべき記事
- set -eなどエラーハンドリングの基本
- 実践:ファイルを自動でバックアップするスクリプトを作る
タグ: Shell, 中級者向け, 実践・自動化