つーことで『cron, crontab, and more』でちまちま悩んでいましたが、思いついたのはまた Ruby ですが以下のものです。やってることは簡単で、要は
複雑な条件はお気に入りの言語で記述する
ってことです。そのために
- 条件の名前と実行するプログラムを与えるだけのスクリプトを用意
- 条件の名前はそのまま(この場合は Ruby の)実行するメソッド名に該当する
- cron には(例えば)以下のように書く
complex-cron -c RULE 'program'
という方法にしてみました。以下のスクリプトでは RULE は省略可能で、その場合は `default' を指定したものとみなしています。
※ 比較的最近の Web アプリでよくあるプラグインと基本的には同じアイディア、だと思ってます。
呼び出すメソッド(つまり判定条件)に引き数を与えることはできません。一瞬悩みましたが必要ないだろうと思いますし、引き数を与えられるようにすると実行時のパラメータの与え方が複雑になります。
実行するスクリプト
(例えばcomplex-cron て名前にしておく)
#! /usr/bin/env ruby
require 'optparse'
Version = '0.1'
class ComplexCron
def initialize
@condfile = '/etc/defaults/cronconds'
@condname = nil
@cond_default = 'default'
@debug = false
accept_options()
@cmd = ARGV.shift
exam_options()
exec_indeed()
end
def exam_options
if ( @cmd.nil? )
raise 'Nothing to do.'
end
if ( !File.exist?( @condfile ) )
raise "File #{@condfile} does not exist."
else
load( @condfile, false )
if ( @condname.nil? )
@condname = @cond_default
elsif ( !ComplexCronConds.instance_methods().include?( @condname ) )
raise "Rule #{@condname} is not defined."
end
end
end
def exec_indeed
if ( ComplexCronConds.instance_method( @condname ) )
if ( @debug )
puts @cmd
else
exec @cmd
end
end
end
def accept_options
opt = OptionParser.new()
opt.on( '-f CONDFILE' ) { |file|
@condfile = file
}
opt.on( '-c CONDNAME' ) { |method|
@condname = method
}
opt.on( '-n', 'dry run' ) { |v|
@debug = true
}
opt.parse!( ARGV )
end
end # of class ComplexCron
ComplexCron.new()
設定ファイルとは名ばかりの Ruby スクリプト
例えば /etc/defaults/cronconds とかいう名前で置いておく。場所はシステムに応じて適当に決めて。
# -*- ruby -*-
module ComplexCronConds
def default
return false
end
end
この中に上で RULE と書いた名前に該当するメソッドを定義していきます。内容はどうぞご自由に。ただしあくまで「条件」をメソッドとして書くので、必ず true か false を返してください。default はあった方がいいかなと思って定義してあるんですけど、実際どう使うのかはあんまりイメージできてません。
これでまぁ、第3水曜だのシステムの負荷の様子だの、Ruby で判別可能な条件はなんでも書けるようになったわけです。あとは必要に迫られたときに考えればいいんじゃないかと。