【cron】crontabで定期実行する方法|書式・PATHの罠・crontab -e/-l/-r

【cron】crontabで定期実行する方法|書式・PATHの罠・crontab -e/-l/-r Linux

cronは、Linuxで「毎日決まった時刻にバックアップを取る」「5分おきに監視スクリプトを動かす」といった定期実行を担う仕組みです。実行するジョブの一覧はcrontab(cron table)というファイルに書き、専用のコマンドcrontabで編集します。サーバー運用では欠かせない存在で、SSHで入ったサーバーの自動化に日常的に使われます。

この記事では、実機のLinux(WSLのDebian)で実際に1分間隔のジョブを登録し、本物の時計の経過を待って、本当に実行されるかを確認しながら、cronの書式と落とし穴を整理します。とくに重要なのが、「ターミナルでは動くのに、cronで動かすと失敗する」という定番のトラブルです。原因の多くはcronのPATHが対話シェルよりずっと狭いことにあります。

先に結論

  • 編集はcrontab -e、確認は-l、全削除は-rです。
  • 書式は「分 時 日 月 曜日 コマンド」の5フィールドです。
  • cronのPATHは対話シェルよりずっと狭い(既定は/usr/bin:/bin程度)。
  • 「ターミナルでは動くのにcronだと失敗する」の多くはこのPATH不足が原因です。
  • コマンドはフルパスで書くか、PATH=行をcrontabの先頭に足すと安全です。
  • 通知メールが不要ならMAILTO=""、出力を消すなら>/dev/null 2>&1を付けます。

スクリプト内でのエラー検知はリダイレクトとパイプ、動いているか確認するプロセス管理はps・kill、環境変数の基本は環境変数とPATHもあわせて参考になります。

スポンサーリンク

crontabの基本操作(-e / -l / -r)

cronのジョブは、ユーザーごとのcrontabファイルに書きます。直接ファイルを編集するのではなく、専用のコマンドcrontabで安全に操作します。

crontab の基本コマンド
# 編集(エディタが開く。既定は vi や nano)
crontab -e

# 登録内容の一覧表示
crontab -l

# 登録をすべて削除
crontab -r

# エディタを指定して編集したいとき
EDITOR=vim crontab -e

実機で確認したところ、crontab -lで登録済みのジョブが一覧表示され、crontab -rを実行すると登録がすべて削除され、直後のcrontab -lno crontab for root(登録なし)と表示されました。crontab -eは対話的にエディタが開く形式で、保存すると自動的に反映されます(デーモンの再起動などは不要です)。-r確認なしで即座に全削除されるため、実行前にcrontab -lで内容を控えておくと安心です。

書式:分 時 日 月 曜日(5フィールド)

crontabの1行は「分 時 日 月 曜日 実行するコマンド」という5つの数値(またはアスタリスク)+コマンドで構成されます。*は「毎回」を意味します。

5フィールドの書式
# 分 時 日 月 曜日  コマンド
# *  *  *  *  *   command  ← 全部 * だと毎分実行

# 毎日 2:30 に実行
30 2 * * * /path/to/backup.sh

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

# 平日(月〜金)の 9:00 に実行(曜日は 0=日, 1=月 ... 6=土)
0 9 * * 1-5 /path/to/report.sh

# 毎月1日の 0:00 に実行
0 0 1 * * /path/to/monthly.sh

# 複数の時刻をカンマで指定(9時と18時)
0 9,18 * * * /path/to/twice.sh

*/5(5分ごと)、1-5(月〜金の範囲)、9,18(9時と18時のリスト)のように、*・範囲(-)・間隔(/)・リスト(,を組み合わせて柔軟に指定できます。曜日は0が日曜、6が土曜です(7を日曜として受け付ける実装もあります)。書式を間違えると意図しない頻度で動いてしまうため、次で紹介する実際の動作確認が重要です。

【実証】本当に定期実行されるかを確認する

「crontabに書いた時刻に本当に動くのか」を、実機で実際の時計の経過を待って確認しました。* * * * *(毎分)でジョブを登録し、時刻を記録するだけの単純なコマンドを仕込みます。

動作確認の登録
# 現在時刻をログに追記するジョブを、毎分実行で登録
(crontab -l 2>/dev/null; echo '* * * * * date >> /tmp/cronjob/out.log 2>&1') | crontab -

# 登録内容を確認
crontab -l
# * * * * * date >> /tmp/cronjob/out.log 2>&1

# しばらく待ってからログを確認
cat /tmp/cronjob/out.log
実際に90秒待って本物の実行を確認

実機でこのジョブを登録し、実際に90秒待ったところ、ログファイルにはSun Jul 5 07:43:01 JST 2026Sun Jul 5 07:44:01 JST 2026という2行の実行記録が残りました。ジョブを登録した時刻(07:42:39)から見て、次の分の境界(07:43)とその次(07:44)で、確かに1分ごとに自動実行されていることが分かります。crontabへの追記はecho '既存の内容'; echo '新しい行'crontab -にパイプする形(既存の登録を消さずに追加する定番の書き方)を使いました。「本当に動くのか不安」なときは、まずこのように時刻を記録するだけの単純なジョブで、動作を実際に目で確認するのが確実です。

【最重要】cronのPATHは驚くほど狭い

cronで最もよく起きるトラブルが、「ターミナルで実行すれば動くのに、cronから動かすと失敗する」という現象です。原因のほとんどはcronが使うPATH(コマンドを探すディレクトリ一覧)が、普段のターミナルよりずっと狭いことにあります。

cronのPATHを確認する
# cron経由でPATHを記録するジョブを登録(PATH行を書かない場合)
echo '* * * * * echo $PATH > /tmp/cronjob/defaultpath.log 2>&1' | crontab -

# しばらく待って中身を見る
cat /tmp/cronjob/defaultpath.log
# /usr/bin:/bin   ← 驚くほど狭い

# 対話シェルのPATH(参考)
echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:...
cronの既定PATHは/usr/bin:/binだけ(実証)

実機で、PATH行を書かずにcronジョブからecho $PATHを実行させたところ、結果は/usr/bin:/binという、たった2つのディレクトリだけでした。対話シェルの$PATHには/usr/local/binなど数十個のディレクトリが含まれているのと比べると、驚くほど狭いことが分かります。「自分でインストールしたコマンド」「nvmやpyenvで入れたバージョン」「npmのグローバルコマンド」などは、この既定PATHに含まれず、cronからはcommand not foundになりがちです。対話シェルでは動くのにcronで動かないときは、まずこのPATH不足を疑ってください。対策は2つあります。1つはコマンドをフルパスで書くこと、もう1つはcrontabの先頭にPATH=行を追加することです。

実際にPATH=/usr/bin:/binという行をcrontabの先頭に足して同じジョブを動かしたところ、記録された$PATHその指定どおりの値になりました。つまりcrontab内のPATH=行は、そのユーザーのcronジョブすべてに適用される設定です。

PATH行で解決する
# crontab の先頭に PATH を明示する
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * my-custom-script.sh

# もしくは、各行でコマンドをフルパスで書く
* * * * * /usr/local/bin/my-custom-script.sh

# スクリプト内で使う環境変数も明示しておくと安全
* * * * * /home/user/bin/backup.sh >> /home/user/backup.log 2>&1

crontabの先頭に書いたPATH=SHELL=などの変数は、そのファイル内のすべてのジョブに適用されます。1行ずつフルパスを書く方法と、先頭でPATHをまとめて広げる方法、どちらでも解決できますが、複数のジョブを管理するなら先頭に1回書くほうが保守しやすいです。

出力の扱いとMAILTO

cronジョブの標準出力・エラー出力は、既定ではローカルのメールとして届く設定になっていることがあります。通知が不要ならMAILTO=""で無効化し、出力自体を捨てたいなら>/dev/null 2>&1を付けます。

MAILTOと出力管理
# メール通知先を指定(空にすると通知しない)
MAILTO=""
* * * * * /path/to/job.sh

# 出力をログファイルに残す(標準出力とエラーの両方)
* * * * * /path/to/job.sh >> /var/log/myjob.log 2>&1

# 出力を完全に捨てる(成否だけ気にする場合)
* * * * * /path/to/job.sh >/dev/null 2>&1

MAILTO=""や出力先の指定はcrontab先頭の変数として書きます。ログを残さず結果も見ないまま放置すると、失敗に気づけません。最低限、>> ログファイル 2>&1標準出力とエラーの両方を記録しておくことをおすすめします。cronの実行記録自体は、多くのディストリビューションで/var/log/syslogjournalctl(systemd環境)にも残るため、「ジョブが動いた形跡があるか」はそちらでも確認できます。

主な書式一覧

crontabでよく使う書き方をまとめます。

書き方 意味
* * * * * 毎分
*/5 * * * * 5分ごと
30 2 * * * 毎日2:30
0 9 * * 1-5 平日9:00
0 9,18 * * * 9時と18時
crontab -e / -l / -r 編集 / 一覧 / 全削除

よくある失敗

ターミナルでは動くのにcronだと動かない

cronのPATHは既定で非常に狭いです。コマンドをフルパスで書くか、PATH=行を追加します。

crontab -rで全部消してしまう

確認なしで即座に全削除されます。実行前にcrontab -lで内容を控えておきます。

曜日と日にちの指定を混同する

5番目は曜日(0=日〜6=土)、3番目は日にちです。混同すると意図しない日に実行されます。

出力を放置して失敗に気づかない

>> ログ 2>&1で記録を残し、定期的に確認します。

1分ごとの指定を*/1と書いてしまう

間違いではありませんが、* * * * *のほうが一般的で読みやすい書き方です。

よくある質問

Qcrontabの書式を教えてください。
A「分 時 日 月 曜日 コマンド」の5つの数値(または*)とコマンドで構成されます。*は「毎回」、*/5は「5分ごと」のような間隔、1-5は範囲、9,18はリストを表します。曜日は0が日曜、6が土曜です。
Q本当に定期実行されているか確認するには?
A時刻を記録するだけの単純なジョブ(例: * * * * * date >> ログ 2>&1)を登録し、実際に数分待ってログの中身を確認するのが確実です。実機でも、90秒待つと2回分の実行記録(1分間隔)がログに残ることを確認しています。
Qターミナルでは動くのにcronで実行すると失敗します。
AcronのPATH(コマンドを探すディレクトリ)が、対話シェルよりずっと狭いことが原因のことが多いです。実機でも、cron経由の既定PATHは/usr/bin:/binだけで、対話シェルには含まれる多くのディレクトリが無いことを確認しています。コマンドをフルパスで書くか、crontabの先頭にPATH=行を追加してください。
Qcrontabの内容を確認・削除するには?
A確認はcrontab -l、編集はcrontab -e、すべて削除するならcrontab -rです。-rは確認なしで即座に全削除されるため、実行前にcrontab -lで内容を控えておくと安全です。
Qcronジョブの通知メールを止めるには?
Acrontabの先頭にMAILTO=""を書きます。また、出力自体を残したくない場合は各ジョブの末尾に>/dev/null 2>&1を付けます。ただし失敗に気づけなくなるため、最低限>> ログファイル 2>&1で記録は残しておくことをおすすめします。

まとめ

  • 操作はcrontab -e(編集)・-l(一覧)・-r(全削除)。
  • 書式は「分 時 日 月 曜日 コマンド」の5フィールドです。
  • cronのPATHは驚くほど狭い(既定は/usr/bin:/bin程度)。
  • 「動かない」ときはフルパス指定かPATH=行の追加で解決します。
  • 出力は>> ログ 2>&1で残し、不要な通知はMAILTO=""で止めます。

cronは、サーバー運用の自動化に欠かせない存在です。「PATHが狭い」という一点さえ押さえておけば、「ターミナルでは動くのにcronだと失敗する」という定番のハマりどころを避けられます。まずは時刻を記録するだけの簡単なジョブで動作を確認し、そこから少しずつ実務のバックアップや監視スクリプトへ育てていくとよいでしょう。