現場で使えるシェルスクリプト入門22:cronによる定期自動実行の実践ガイド

シェルスクリプト Linux

前回の記事(入門21)で、標準出力・標準エラー出力の分離や、リダイレクト(2>&1)、そして画面表示とファイル保存を両立する tee コマンドによるログ保存テクニックを習得しました。

ログを保存する手法が分かったところで、シェルスクリプトを人の手を介さずに決まったスケジュールで自動実行させる「定期バッチ処理」へとステップアップしましょう!

LinuxやUnix環境でタスクの定期自動実行を担う標準機能が「cron(クロン・クーロン)」です。バックアップの作成、ログのローテーション、外部APIからの定期データ収集など、現場の自動化に欠かせない仕組みです。
しかし、初心者が陥りがちなのが「ターミナルから手動で叩くと正常に動くのに、cronに登録するとなぜか動かない・失敗する」という環境差のトラブルです。

今回は、cronの基本構文から、現場で直面する代表的な問題の回避策、安全な二重起動防止(flock)、そして前回の知識を結集したログ出力設計までを体系的に解説します!

cronの仕組みと設定管理(crontabコマンド)

Linuxシステムでは、バックグラウンドで「crond」という常駐プロセス(デーモン)が稼働しています。このcrondが毎分ごとに設定ファイルを巡回し、指定された日時に合致したコマンドを自動実行します。

ユーザーがcronのスケジュールを登録・管理するには、専用のcrontab(クロンタブ・クーロンタブ)」コマンドを使用します。

# 現在ログインしているユーザーのcron設定を編集する(エディタが開く)
crontab -e

# 現在登録されているcron設定の一覧を表示・確認する
crontab -l

crontab -e を実行すると、環境変数 EDITOR(デフォルトではviやnano)で設定ファイルが開き、スケジュールと実行コマンドを1行ずつ記述できます。

【注意点:crontab -r の誤入力リスク】
登録されている設定を一括削除するコマンドとして crontab -r が存在します。しかし、確認メッセージなしで設定が即座に消去されてしまうため、キーボードの押し間違い(-e-r の隣接)で事故が発生します。十分ご注意ください。

cronのスケジュール構文(5つの時刻フィールド)

crontabファイル内では、「5つの時刻指定フィールド」の後に、実行したいコマンドを空白区切りで記述します。

フィールド指定可能な値意味
第1フィールド0 - 59分(何分に実行するか)
第2フィールド0 - 23時(何時に実行するか)
第3フィールド1 - 31日(何日に実行するか)
第4フィールド1 - 12月(何月に実行するか)
第5フィールド0 - 7曜日(0と7は日曜日、1=月、2=火…)

記述フォーマットは以下の通りです。

# [分] [時] [日] [月] [曜日] [実行するコマンド]
* * * * * /path/to/command

特殊記号の使い方

数字だけでなく、以下の特殊記号を組み合わせることで柔軟なスケジュールを設定できます。

  • *(アスタリスク):すべての値(毎分、毎時、毎日など)。
  • ,(カンマ):複数の値を指定(例: 0,30 ➔ 0分と30分)。
  • -(ハイフン):範囲を指定(例: 1-5 ➔ 月曜日から金曜日まで)。
  • /(スラッシュ):間隔(ステップ)を指定(例: */15 ➔ 15分ごと、*/2 ➔ 2時間ごと)。

実務で頻出するスケジュール設定例

# 1. 毎時0分(1時間ごと)に実行
0 * * * * /path/to/script.sh

# 2. 毎日深夜3時0分に実行
0 3 * * * /path/to/backup.sh

# 3. 15分ごとに実行
*/15 * * * * /path/to/check.sh

# 4. 平日(月〜金)の朝9時0分に実行
0 9 * * 1-5 /path/to/work_start.sh

# 5. 毎月1日と15日の午前4時0分に実行
0 4 1,15 * * /path/to/clean.sh

手動では動くのにcronだと動かない場合の対策

手動で ./my_script.sh と実行した際は問題なく動作したのに、cronに登録すると何も起きない、あるいはエラーで失敗することがあります。
その原因のほとんどは、「ターミナルのログイン環境」と「cronの実行環境」の差異にあります。

1. 環境変数(PATH)の欠落

私たちがターミナルで作業する際は、.bashrc.zshrc などによって /usr/local/bin や独自ツールのパスが環境変数のPATHに登録されています。
しかし、cronがスクリプトを実行する環境では、最小限の PATH(通常は /usr/bin:/bin 程度)しか設定されていません
そのため、手動では動いていた jqdocker、独自コマンドが「command not found」となって異常終了します。

【対策】
crontabファイルの冒頭で PATH を明示的に宣言するか、スクリプト内でコマンドをフルパスで呼び出します。

# crontab の最上部に PATH を定義しておく
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# スケジュール設定
0 3 * * * /home/user/scripts/backup.sh

2. カレントディレクトリの不一致

ターミナルでは「スクリプトがあるディレクトリに移動してから実行」することが多いため、相対パス(./config.env./logs/app.log)が正しく読み込まれます。
しかし、cronがスクリプトを呼び出す際のカレントディレクトリは、通常「実行ユーザーのホームディレクトリ(/home/username)」になります。
そのため、スクリプト内で相対パスを使っていると「ファイルが見つかりません」というエラーになります。

【対策】
スクリプトの冒頭で、「そのスクリプト自身が存在するディレクトリ」へ移動するコードを記述しておきます。

#!/bin/bash

# スクリプト自身の配置ディレクトリへカレントディレクトリを移動
cd "$(dirname "$0")" || exit 1

# ここから先は相対パス(./config.env 等)が手動実行と同じように通用する
source ./config.env

3. 実行ユーザー権限の違い

一般ユーザーのcrontab(crontab -e)で登録したスクリプトは、そのユーザーの権限で動作します。もしシステム設定の変更や特権ディレクトリへの書き込みが必要な処理が含まれている場合、権限不足(Permission denied)で失敗します。

管理者権限が必要なシステムメンテナンス処理は、rootユーザーのcrontab(sudo crontab -e)で登録するか、設定ファイル /etc/crontab 等にユーザーを指定して記述します。

二重起動の防止とログ出力設計

cronを運用する上で、避けるべきトラブルが「処理の二重起動」です。

flockコマンドによる二重起動防止(排他制御)

例えば「10分ごとに実行される集計バッチ」があったとします。普段は3分で終わる処理でも、データ量の一時的な増大やネットワーク遅延により、処理に15分かかってしまった場合、前回の処理が終わっていないのに次の処理が重なって起動してしまいます
プロセスが多重起動すると、CPUやメモリを食い潰してサーバーが高負荷に陥ったり、データベースの二重更新やファイル破壊の原因になります。

これを防ぐ標準的な手法がflock(エフロック)」コマンドによる排他制御(ロック)です。

# flock を使って前回の処理が終わるまで多重起動をブロックする
*/10 * * * * flock -n /tmp/batch_process.lock /home/user/scripts/heavy_job.sh

flock -n <ロックファイル> <コマンド> と指定すると、指定したロックファイルを確保しようと試みます。

  • 前回の処理がまだ動いている場合:-n(ノンブロッキング)オプションにより、待機せずに新しいプロセスを即座にスキップ(終了)させます。
  • 前回の処理が終わっている場合:ロックを取得して安全にスクリプトを実行し、スクリプト終了時にロックを自動解放します。

【注意点:flockコマンドのパス指定について】
Linux環境(UbuntuやRed Hat系など)では、通常 flock/usr/bin/flock に配置されています。cron設定の先頭で PATH を定義している場合や、システムの標準探索パスに含まれている環境では flock と記述するだけで動作します。
ただし、cronの環境差による「command not found」エラーを防ぐため、事前に which flock で配置場所を確認し、/usr/bin/flock のようにフルパスで記述するか、crontabの冒頭でPATHを定義しておく運用が推奨されます。

【コラム:入門17で紹介した「mkdir」方式との使い分け】
入門17では、スクリプト内部で mkdirtrap を使って自前でロックを管理する方法を解説しました。それぞれの特徴と使い分けの目安は以下の通りです。

  • flockコマンド(cron運用におすすめ)
    crontabの1行でスクリプトの外側から手軽に排他制御を適用できます。OSカーネルのファイルロック機能を利用しているため、仮にプロセスが強制終了(SIGKILLなど)された場合でも、OSが自動でロックを解放してくれるため、自動運用との相性が良いです。
  • mkdir + trap方式(スクリプト単体での完結)
    外部コマンド(flock)が用意されていない環境や、シェルスクリプト単体で二重起動防止機能を完結させたい場合に有効です。

入門21の知識を活かしたログ出力設計

cronで実行されたスクリプトが画面に出力した内容は、デフォルトではどこにも表示されず、サーバー内のローカルメール(mailコマンド)に溜まって見落とされがちです。
失敗したときに原因を素早く突き止められるよう、前回の入門21で学んだ「リダイレクト(>> log 2>&1)」をcrontabの行末に記述しておく運用を推奨します。

# 【実務の推奨パターン】標準出力もエラーもすべてログファイルへ追記保存
0 3 * * * /home/user/scripts/backup.sh >> /var/log/my_backup.log 2>&1

実践:定期バックアップスクリプトのcron自動運用

それでは、これまでに学んだ内容を結集して、自動バックアップタスクをcronに登録してみましょう。

1. 実行対象スクリプトの準備(backup_task.sh)

前回の入門21で作成したタイムスタンプ付きロギング機能と、ディレクトリ移動を組み込んだサンプルスクリプト(backup_task.sh)です。
スクリプト内ではファイルへ直接書き込まず、メッセージを標準出力・標準エラー出力へ流す設計にしています。これにより、手動実行時は画面で確認でき、cron実行時はcrontab側のリダイレクト(>>)によって1行ずつ保存され、二重書き込みを防ぐことができます。

#!/bin/bash

# エラーハンドリング設定
set -euo pipefail

# スクリプト自身の配置ディレクトリへカレントディレクトリを移動
cd "$(dirname "$0")" || exit 1

log_message() {
    local level="$1"
    local message="$2"
    local timestamp
    timestamp=$(date +'%Y-%m-%d %H:%M:%S')
    echo "[${timestamp}] [${level}] ${message}"
}

log_info() { log_message "INFO" "$1"; }
log_error() { log_message "ERROR" "$1" >&2; }

# バックアップ処理
log_info "--- 定期バックアップタスクを開始 ---"

# 擬似バックアップ:重要な設定ファイルを日付付きでアーカイブ
BACKUP_DIR="./backups"
mkdir -p "${BACKUP_DIR}"
BACKUP_NAME="${BACKUP_DIR}/config_backup_$(date +'%Y%m%d_%H%M%S').tar.gz"

if [ -f "./config.env" ]; then
    tar -czf "${BACKUP_NAME}" ./config.env
    log_info "アーカイブ作成完了: ${BACKUP_NAME}"
else
    log_error "バックアップ対象の config.env が見つかりません。"
    exit 1
fi

log_info "--- 定期バックアップタスクが正常に終了しました ---"

スクリプトを作成したら、実行権限(chmod +x)を付与し、ログの出力先ディレクトリを作成しておきます。

chmod +x /home/user/scripts/backup_task.sh
mkdir -p /home/user/scripts/logs

2. crontabへの登録

crontab -e を実行し、末尾に以下のように登録します。

# PATHの定義
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# 毎日深夜2時0分に、二重起動を防ぎながらバックアップを実行し、実行ログを記録
0 2 * * * flock -n /tmp/backup_task.lock /home/user/scripts/backup_task.sh >> /home/user/scripts/logs/cron_backup.log 2>&1

3. 実行結果の確認

指定した時刻が過ぎた後、ログファイルを確認します。

cat /home/user/scripts/logs/cron_backup.log
[2026-09-20 02:00:01] [INFO] --- 定期バックアップタスクを開始 ---
[2026-09-20 02:00:01] [INFO] アーカイブ作成完了: ./backups/config_backup_20260920_020001.tar.gz
[2026-09-20 02:00:01] [INFO] --- 定期バックアップタスクが正常に終了しました ---

人の手を介さずに、指定時刻にタスクが自動起動し、ログとアーカイブファイルが正しく生成されていることが確認できます。

まとめ

今回は、Linux環境でタスクをスケジュール自動実行する「cron」の基本から、手動実行との差異による対策、flock による二重起動防止、安全なログ設計までを解説しました。

  • cronの構文:分・時・日・月・曜日の5つのフィールドを理解し、*/15, などを使い分ける。
  • 環境差対策:crontabでの PATH 定義と、スクリプト内での cd "$(dirname "$0")" を取り入れる。
  • 運用の安全性確保:二重起動は flock で防ぎ、実行結果は >> log 2>&1 でログに記録する。

cronを適切に制御できるようになると、サーバーが休まずにスクリプトを動かし続けてくれます。これまで学んだ知識を組み合わせ、ぜひ日々の運用業務や定型作業の自動化に役立ててみてください!

コメント

タイトルとURLをコピーしました