本文へスキップ
BecomeCoder

シェルスクリプトコース · 第8章 cron で定期実行 · レッスン37

cron の落とし穴 ― 手元で動くのに cron で動かない

ブラウザで完結

導入

「手で打つと動くのに、cron に登録すると動かない」。cron の相談の9割はこれです。原因はほぼ決まっていて、環境が違うの一言に尽きます。先に知っておけば、丸一日を溶かさずに済みます。

説明

cron がスクリプトを実行するときの環境は、あなたがログインして打つときとは別物です。

項目ログインして打つときcron が実行するとき
PATH/usr/local/bin などフルセット最小限/usr/bin:/bin 程度)
カレントディレクトリいまいる場所ホームディレクトリ
環境変数.bashrc などが読み込まれた状態ほとんど無い
画面ある(出力が見える)無い(出力は消えるかメールに)

この違いが、そのまま3つの落とし穴になります。

#!/bin/bash

# 落とし穴1: 相対パスは「ホームからの相対」になる
# 悪い例: cat logs/app.log   ← cron ではホーム基準になり、意図とずれる
# 良い例: 絶対パスで書く
cat /home/user/logs/app.log | wc -l

# 落とし穴2: コマンドが見つからない(PATH が短い)
# 良い例: スクリプトの冒頭で PATH を自分で設定する
PATH=/usr/local/bin:/usr/bin:/bin
echo "PATH: $PATH"

# 落とし穴3: 出力が消える
# 良い例: ログファイルに残す(第3章のリダイレクト)
echo "処理しました" >> /home/user/cron.log 2>&1

対策は3つとも単純です。

  1. パスは絶対パスで書く/home/user/scripts/backup.shcd /home/user/files を冒頭に置くのも可)
  2. PATH を自分で設定する(スクリプトの冒頭、または crontab の先頭行に PATH=…
  3. 出力をログに残す>> /var/log/mybatch.log 2>&1

crontab の書き方としては、こうなります。

PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * /home/user/scripts/backup.sh >> /home/user/backup.log 2>&1
flowchart TD
    A["手元では動く"] --> B{"cron で動かない"}
    B --> C["相対パス?<br/>→ 絶対パスにする"]
    B --> D["コマンドが見つからない?<br/>→ PATH を設定"]
    B --> E["何も起きない?<br/>→ 出力をログに残して確認"]

もう1つ、% は crontab では特別な意味(改行)を持ちます。date +%Y-%m-%d をそのまま crontab に書くと壊れるので、\% とエスケープするか、そもそもスクリプトの中に書いてしまうのが安全です。cron には「スクリプトを呼ぶだけ」を登録する、と決めておくと、この手の罠をまとめて避けられます。

やってみよう

スクリプトを実行して、絶対パスでの読み込みと PATH の設定を確認しましょう。次に端末で cat cron.log として、追記されたログを見てください。そのうえで、上のスクリプトを crontab に登録して cronsim で動かすと——cron から実行されても、絶対パスなら正しく動くことが確かめられます。

演習

/home/user/logs/app.log を絶対パスで読んで行数を表示してください(8行です)。

ヒント1を見る

相対パスの logs/app.log/home/user/logs/app.log に変えます。

ヒント2を見る

cat /home/user/logs/app.log | wc -l

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

シェルスクリプト(シミュレート)

スクリプトを書くエディタと、それを動かす仮想シェルを読み込みます(本物のbashではなく、動きを再現した学習用の環境です)。
スクロールして表示された時点でも自動で読み込まれます。