スポンサーリンク

bashでcronジョブを自動登録する (Linuxバッチでcrontabを編集)


Linux上で,スケジュールされたタスクを実行するためには,cronが必要。


そのcronタスク設定操作を自動化するには,どうしたらよいか?


本記事では,以下の点を扱う。


crondのインストール(手動)

crontabが入ってても,crondが無かったりする。

crondのインストール状況の確認方法:

/etc/init.d/crond status

※または /sbin/service crond status

もし無効であれば,crondをインストールする必要がある。

crondのrpm(vixie-cron)をダウンロードして取得
http://whatislinux.net/rpm/vixie-cron...
RPM: vixie-cron-4.1-76.el5.i386.rpm

インストール

rpm -ivh vixie-cron-4.1-76.el5.i386.rpm

インストールされたことを確認

rpm -qa | grep vixie-cron


これで,以下のファイルやフォルダが生成される。

  • /etc/init.d/crond (起動スクリプト)
  • /var/spool/cron (ジョブ定義ファイルの設置ディレクトリ)

なお,アンインストールは下記。

rpm -e vixie-cron-4.1-76.el5.i386

削除時はrpmの拡張子を含めない点に注意。


crondのインストール(自動)

前項のインストールを,シェルで自動化しよう。


インストール用のシェル:

rpmと同一フォルダに,install_crond.sh

#!/bin/sh


# rpmのパス
rpm_file="vixie-cron-4.1-76.el5.i386.rpm"


## インストール実行


# 既にインストールされているかどうかで分岐
# rpmのリストの行数が0行より大きいかどうかを判定
if [ `rpm -qa | grep vixie-cron | wc -l` -gt 0 ] ; then
  # インストール済みだった場合
  echo "already installed."
else
  # 無かった場合
  echo "not yet installed. executing install..."

  # インストール実行
  rpm -ivh ${rpm_file}
fi


## インストールが完了していることを確認


# RPMの数
echo "number of rpm:"
[ `rpm -qa | grep vixie-cron | wc -l` -gt 0 ] && echo "OK" || echo "NG"

# 起動スクリプトがあるか
echo "init script:"
[ -f /etc/init.d/crond ]                      && echo "OK" || echo "NG"

# フォルダがあるか
echo "/var/spool/cron :"
[ -d /var/spool/cron ]                        && echo "OK" || echo "NG"


echo "install script finished."

アンインストール用のシェル:

#!/bin/sh


# rpmの識別キー
rpm_key="vixie-cron-4.1-76.el5.i386"


## アンインストール実行


# 既にインストールされているかどうかで分岐
if [ `rpm -qa | grep vixie-cron | wc -l` -gt 0 ] ; then
  # インストール済みだった場合
  echo "already installed. executing uninstall..."

  # アンインストール実行
  rpm -e ${rpm_key}
else
  # 無かった場合
  echo "not yet installed."
fi


## アンインストールが完了していることを確認


# RPMの数
echo "number of rpm:"
[ `rpm -qa | grep vixie-cron | wc -l` -eq 0 ] && echo "OK" || echo "NG"

# 起動スクリプトがあるか
echo "init script:"
[ ! -f /etc/init.d/crond ]                    && echo "OK" || echo "NG"

# フォルダがあるか
echo "/var/spool/cron :"
[ ! -d /var/spool/cron ]                      && echo "OK" || echo "NG"


echo "uninstall script finished."

cronにジョブ登録(手動)

rootユーザの場合,/var/spool/cron/root というファイルの中に,1行ずつジョブを記述する。

そうすると,登録されたジョブはcronによって「rootユーザとして」実行されることになる。

http://q.hatena.ne.jp/1139540811#

  • /etc/crontab:システムジョブ(デイリーログローテーションやデイリーバックアップなど)で使用するもので、全てroot権限で実行されます。
  • /var/spool/cron/:ユーザジョブと言われるもので、許可された各ユーザごとのcronが入ります。実行ユーザは各ユーザ


ここでは,

「毎分,/tmp内に,現在時刻をファイル名に持つようなファイルを作成する」

というジョブを登録する。



/var/spool/cron/root

* * * * * touch /tmp/hoge_`date +\%Y\%m\%d\%H\%M\%S`

「*」は順に,分,時,日,月,曜日を表す。



はまりやすい点は2つある。

(1)各ジョブの行の末尾には改行(LF)が必要。

manpage of Ubuntu
http://manpages.ubuntu.com/manpages/g...

  • cronではcrontabの各エントリの末尾に改行文字があることが必要になる
  • 改行文字で終わっていないコマンドは絶対に実行されない。
  • 間違いを防ぐためには、crontabの末尾に必ず空行を入れるようにするのが一番よい。


(2)ジョブとして実行したいコマンドの「%」は,エスケープする必要がある。

crontab「第 6」フィールド中の'%'は改行として解釈される
http://d.hatena.ne.jp/turutosiya/2009...

  • コマンド中にパーセント記号 (%) が バックスラッシュ (\) によってエスケープされずに置かれていると、 改行文字に置き換えられ、最初に現れた % 以降の全てのデータは 標準入力としてコマンドに送られる。


crontabのよくあるミス
http://ja.wikipedia.org/wiki/Crontab#...

  • よくあるミスのひとつは、コマンド指定においてエスケープせずに「%」記号を使うことで、これはエスケープする必要がある。

※ベターなのは,crontabファイル内に直接コマンドを書かずに,シェルスクリプトを呼び出す形式にすることだ。
そのほうが変更しやすく保守性が保てる。



ジョブ定義ファイルを設置したら,crondを再起動しておく。

/etc/init.d/crond restart

これで即時,ジョブ定義ファイルがリロードされ,次の分の01秒から毎分ジョブが実行される。


cronにジョブ登録(自動)

ここが本題。


「cronにジョブを登録するコマンド」というと「crontabコマンド」がある。

crontab -e は,単に「エディタでcrontab設定ファイルを開く」コマンドでしかない。

crontab ファイル名 とすれば,ファイルからジョブを登録することもできる。

恐怖のcrontab -r. 設定ファイルはレポジトリ管理せよ
http://d.hatena.ne.jp/LukeSilvia/2008...

  • crontabの定期的なバックアップ出力をcrontabしておく
  • crontabへの登録内容を特定の場所に置いといて,そのファイルを編集してcrontabコマンドで読み込ませるように


Linuxのcrontabコマンドの脆弱性をつぶす
http://codezine.jp/article/detail/5360

  • crontabファイルとcrontabコマンドの違い
  • マニュアルページのセクション番号を付けて、設定ファイルはcrontab(5)、コマンドはcrontab(1)などと表記します。

また,下記のように直接ファイル操作する方法もある。

これだと,「同じタスクが重複して登録されないようにチェックする」など細かい操作が可能。


add_cron_job.sh

#!/bin/sh


# 登録したいジョブ
cron_job_line="* * * * * touch /tmp/hoge_\`date +\\%Y\\%m\\%d\\%H\\%M\\%S\`"

# crontabファイル
cron_file="/var/spool/cron/root"



## crontabファイル準備


# 無ければ作る
[ -f ${cron_file} ] && touch ${cron_file}



## タスク登録


# 既に登録されているかどうかを判定
cron_job_line_for_grep="${cron_job_line//\\/\\\\}"
if [ `grep "${cron_job_line_for_grep}" "${cron_file}" | wc -l` -eq 0 ] ; then
  echo "not registered yet. begin registering..."
  
  # 追記
  echo "${cron_job_line}" >> "${cron_file}"
else
  echo "already registered."
fi


# cron再起動
/etc/init.d/crond restart


echo "registering finished."


細かな注意点をいくつか。
(bashコーディング上のポイント)


(1)""で囲まれた文字列中の``はエスケープしないと即時実行されてしまう。

→ "\`"のようにする。


(2)バックスラッシュが1個だけだと,grepは別の意味に解釈してしまう。

→"${cron_job_line//\\/\\\\}"のように全置換して2個に増やす。


(3)echo * だと,アスタリスクが「カレントディレクトリの全ファイル名」に展開されてしまう。

→echo "*" のようにくくる。



補足:cronのログのローテーション

cronのログは,/var/log/cron である。

cronに「毎分実行」のタスクが登録されていたら,cronのログは肥大化しやすくなる。

ログのローテーション設定はどうなっているか。

(1)crontab設定ファイル

/etc/crontab

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly


/etc/cron.dailyフォルダ内の全タスクを実行するように設定されている。

システム設定ファイルを知ろう〜/etc/crontab〜
http://www.itmedia.co.jp/help/tips/li...


run-partsとは
http://d.hatena.ne.jp/hogem/20090331/...
指定ディレクトリ以下のスクリプトを順次実行するコマンド

(2)/etc/cron.dailyフォルダ

logrotateという名前のシェルが置いてある。

#!/bin/sh
/usr/sbin/logrotate /etc/logrotate.conf

/etc/logrotate.confという設定ファイルを参照している。

(3)/etc/logrotate.confファイル

ログのローテーション設定ファイル。

# see "man logrotate" for details
# rotate log files weekly

weekly

# keep 4 weeks worth of backlogs rotate 4
# send errors to root errors root
# create new (empty) log files after rotating old ones create
# uncomment this if you want your log files compressed
#compress
# RPM packages drop log rotation information into this directory

include /etc/logrotate.d

# no packages own lastlog or wtmp -- we'll rotate them here

/var/log/wtmp
{
  monthly
  create 0664
  root utmp
  rotate 1
}

/etc/logrotate.dを参照している。

(4)/etc/logrotate.dフォルダ

syslogというファイルがある。logrotateの設定ファイルの一種。

/var/log/messages /var/log/secure /var/log/maillog
/var/log/spooler /var/log/boot.log /var/log/cron {
  sharedscripts
  postrotate
  /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
  endscript
}


/var/log/cronをローテーション対象として指定している。
(※crondをインストールする前から/var/log/cronがローテーション対象になっている。)


記法は以下を参照:

logrotateによるログのローテーション
http://linux.kororo.jp/cont/server/lo...

  • sharedscripts は、以降に記述された処理をワイルドカードの指定に関わらず、1度だけ実行するという宣言文です
  • syslogにHUPシグナルを送り、再起動をすることで、ローテーション後に create によって作成されたファイルに正常にsyslogがログを吐き出すようにしている


syslogの保存期間を変更するには
http://www.atmarkit.co.jp/flinux/rens...


syslogについての補足。


cronは,ログを出力する際に「syslog」を使う。

(Javaアプリがログ出力のためにLog4jを使うのが標準なのと同じように,Linux上の多くのアプリケーションがsyslogを経由してログ出力する。)


syslogは,設定ファイルsyslog.confの中で,cronのログを/var/log/cronに出力するようになっている。

なので,cronのログはそこに出力される。

# Log cron stuff
cron.*           /var/log/cron

/etc/syslog.conf設定
http://variable.jp/linux/2007/11/etcs...