インフラエンジニアが避けては通れないcrontab登録作業.登録時に一番よくぶつかるのは,普通にコマンドライン上からは実行されるのに,crontabで実行すると全く実行されないという現象だと思う.今回はこの現象にぶちあたった際に確認することをまとめた.自分で言うのもなんだがWeb上にあふれている類似ページの中で一番クオリティが高いと自負している.きっと他には書かれていない内容がここにあると信じている.今後検索結果の上位に上がってくるページはきっとこのページよりも新しい日付のものしか存在しないと信じている.というわけで読んでね.

環境


  • Ubuntu 18.04 LTS

※ 基本的に他のLinuxでも参考になると思う.

確認項目


その1: コマンドライン上で実行できるか


猫も杓子もまずはシェルスクリプトがコマンドライン上で実行できていないと話にならない. この際,crontabに登録する際に記述する通り絶対パスで実行することをおすすめする.

たとえば,crontabで以下のように登録するのであれば

* * * * * /home/human/scripts/yaho.sh > /dev/null 2>&1

以下がコマンドライン上で期待通りに動作することを確認する.

/home/human/scripts/yaho.sh

この際,実行されなかった場合にはシェルスクリプトの実行権限が付与されているか確認する.

ls -l /home/human/scripts/yaho.sh
--> 実行権限が付与されているか確認

# なければ付与する
chmod u+x /home/human/scripts/yaho.sh

シェルスクリプト作成後に付与し忘れているケースが多い.まぁ,これは初歩の初歩.

なお,この時点でシェルスクリプトの挙動がおかしくなった際には変数に期待通りの値が設定されているかを確認する.

# bashシェルスクリプトの場合
bash -x /home/human/scripts/yaho.sh

このように-xオプションをつけて実行すると変数の値が確認できる. ちなみにシェルコマンド(この場合はの先頭のbash)はシェルスクリプトで指定しているものと合わせる(/bin/sh場合はsh -xとする)

その2: crontabで実行できるか


コマンドライン上での動作確認が完了したら次はいよいよcrontabでの動作確認である.

* * * * * /home/human/scripts/yaho.sh > /dev/null.log 2>&1

この結果,特に問題なく動作すれば良いが,動かない経験がよくあると思う.次からはコマンドライン上では動作するのに crontabでは動作しない場合に確認する項目を書く.

その3:crontab越しのシェルスクリプトの実行結果の確認


crontab越しに実行されない時,まず確認すべきはどんな結果で終わっているかを確認することである. 以下のように,実行結果をエラーも含めてファイルに出力するようcrontabを変更し,実行時間経過後ファイルの中身を確認する.

* * * * * /home/human/scripts/yaho.sh > /tmp/tempra.log 2>&1

実行開始時間が過ぎたらファイルが生成されているか確認する

ls -l /tmp/tempra.log
cat /tmp/tempra.log

何かしら出力結果にエラーの原因が書かれていればそれを解消する.

よくあるエラーの一例

よくある例として先にも述べたSHELL変数の値の違いに起因するものがある. crontab上からの実行はデフォルトでは/bin/shであるために,/bin/bashなど,他のシェルで実行させたい場合にはシェルスクリプトの一行目にシバン(#!)につづいて指定する必要がある

#!/bin/bash

echo "yaho"

このことを忘れておもてなし心からかシェルスクリプトの仕上げに無邪気にシェルスクリプトの説明を冒頭に書いちゃったりするケースがある.

#--------------------
# This script is yaho
#--------------------
#!/bin/bash 

echo "yaho"

この場合,#!/bin/bash(上記4行目に書かれている場合)は単なるコメントと認識されるため,シェルスクリプト実行時にSHELL変数の値のシェルで実行されてしまうことによりコマンドライン上ではSHELL変数がbin/bashのため問題なく動作していたシェルスクリプトが,crontabだとSHELL変数が/bin/shとなることで/bin/shで無効な文法で書かれている部分でエラーが発生してしまう.シェルスクリプトが実行されない時は一行目を再確認すること. もしくは,以下のようにcrontabで実行する際のシェルをコマンドライン上のSHELL変数と同じ値に指定して対応しても良い.

SHELL=/bin/bash
* * * * * /home/human/scripts/yaho.sh > /dev/null 2>&1

その4: crontab越しのシェルスクリプト実行時の環境変数の値を確認


その3にかぶる部分も多いが,続いて確認すべきはコマンドライン上の環境変数とcrontab上の環境変数の差分である. 手順は以下の通り.

コマンドライン上の環境変数をファイルに出力する.

env > /tmp/env_commandline.log

続いてcrontab上での環境変数を以下のようにファイルに出力する.

* * * * * env > /tmp/env_crontab.log

ファイルが作成されていることを確認できたら両者の差分を確認する.

diff /tmp/env_commandline.log /tmp/env_crontab.log
--> 差分を確認

差分で代表的なものは先にも述べたとおりSHELL変数の値で,コマンドライン上は/bin/bashであるのに対し,crontab上ではデフォルトが/bin/shである.

その5: crontabの編集画面を閉じる.syslogを見る


本記事の最大の目玉はここである.基本的にcrontab -eで編集すると,エディタ操作(viなど)になるため,まるで普通のファイルを編集している気分になり viで:wで保存すれば変更が反映されると思い込みがちであるがそれは違う. crontab -eで編集後,:qで編集を終了しないと変更内容は反映されない. このことはsyslogから確認することができる. ためしにターミナルを2つ開き,1つ目の画面で以下のコマンドを打ち,2つ目の画面でcrontabを開いてみる.

画面1で実行するコマンド

tail -f /var/log/syslog

画面2で実行するコマンド

crontab -e

こうすると,crontab -eを実行した際に以下のメッセージが出力される.

Oct 10 10:51:26 mymachine crontab[16568]: (human) BEGIN EDIT (human)

crontabを編集し,閉じると以下のメッセージが出力される.

Oct 10 10:51:50 mymachine crontab[16568]: (human) REPLACE (human)
Oct 10 10:51:50 mymachine crontab[16568]: (human) END EDIT (human)

このREPLACE, END EDITのメッセージを以て初めてcrontabの変更内容は反映される. crontabの内容を更新したのに反映されていない場合にはcrontabが開きっぱなしになっていないかを確認すると良い.

そして,実行された際には以下のようにsyslogは出力してくれるため,そもそも実行されているかどうかも確認できる.

Oct 10 10:52:01 mymachine CRON[19226]: (human) CMD (/home/human/scripts/yaho.sh >/dev/null 2>&1)

ちょいネタその1:/bin/bashと/bin/shは違う

ちょくちょく,/bin/bash/bin/shの違いでエラーが出ることがあると述べてきた. まずはじめに/bin/shは確認すれば分かるが実体は/bin/dashである.

ls -l /bin/sh
lrwxrwxrwx 1 root root 4 12月 24  2019 /bin/sh -> dash

bashとdashとの違いについては以下にまとめてあるのが詳しい.

ちょいネタその2:crontab作業の注意点

基本的にcrontab -eでの作業は推奨されない. 理由はキーボードの配列にある.eの隣にrがあるため,うっかりcrontab -rとミスタイプしてしまうことで編集どころかcrontabの中身を消してしまう悲劇が起きるからだ.

そのためたとえば以下のようにcrontab -eを使用せずに登録するのがオペレーションとしては理想である.

# バックアップの作成
crontab -l > /tmp/crontab.lst.$(/bin/date +"%Y%m%d")

# 置き換えるファイルの用意
cp /tmp/crontab.lst.$(/bin/date +"%Y%m%d) /tmp/crontab.lst.new

cat <<'EOF' >> /tmp/crontab.lst.new
* * * * * /home/human/scripts/yaho.sh > /dev/null 2>&1
EOF

# 差分を確認
diff <(crontab -l) /tmp/crontab.lst.new

# 置き換え
crontab /tmp/crontab.lst.new

# 置き換えられたことを確認 ( 差分がないことを確認 )
diff <(crontab -l) /tmp/crontab.lst.new

しかしながら,ローカルの開発環境など,オペミスがクリティカルでない環境ではそんなに慎重になる必要はないのでオペミスが及ぼす影響度合いを考えて選択すればよろし.

最後に

これから先諸君が何度もcrontab登録作業に悩まされることがないことを切に願う.