Wiki2
RecurringEvent

Do recurring operation with cron and ruby

cron is very suitable to do recurring events on e.g. a server. But what if you want do an recurring event with a certain minimal interval in a desktop machine which isn’t running all the time.

A backup script is a typical use case where you want to run the operation if a certain time has passed since the last the last time the operation executed.

The idea is to call a script regularly with cron (e.g. every 10 minutes). The script keeps track on when the operation was called last time and only execute the operation again if a minimal set interval has passed.

A simple implementation has been made in ruby below.

#!/usr/local/bin/ruby

require 'date'
require 'digest'
require 'tmpdir'

def command_tmp_file(command)
  return "#{Dir.tmpdir}/last-call-#{Digest::MD5.hexdigest command.join("")}.date"
end

def set_last_time(command)
  fname = command_tmp_file(command)
  open(fname, 'w') { |f|
    f.puts DateTime.now.rfc3339()
  }
end

def seconds_since_last_time(command)
  fname = command_tmp_file(command)
  unless File.exists? fname then return -1 end 
  return Time.now.to_i - DateTime.rfc3339(File.read(fname)).to_time.to_i
end

def print_help
  puts "Call \"wrapped\" command only if a minumum time (seconds) has passed     since it was last executed"
  puts "This utility may be called periodically (e.g. from cron) to achieve periodic execution"
  puts "Usage: #{$0} INTERVAL COMMAND [ARGS]"
  puts "Example: #{$0} 60 echo \"one minute since last call\""
end

if (ARGV.size < 2) || (not ARGV[0] =~ /^[0-9]+$/)
  print_help
  exit
end

command = Array.new(ARGV); command.shift
delay = seconds_since_last_time(command)
min_delay = ARGV[0].to_i

if delay == -1 || delay > min_delay
  puts `#{command.join(" ")}`
  set_last_time(command)
end

If this script is called multiple times the operation (echo "min 10 secs") is only executed if 10 seconds has passed since last time it (i.e. the wrapped operation) was executed.

$ ./interval.rb 10 echo "min 10 secs"               
hello
$ ./interval.rb 10 echo "min 10 secs" 
$ ./interval.rb 10 echo "min 10 secs" 
hello

Is there an existing UNIX utility for this …