Reputation: 11473
Is it possible to find all entries in a crontab that run between time X and time Y without having to parse the cron time entries myself? I'm mainly concerned with time hour and minute, not so much the other 3 time fields.
Upvotes: 4
Views: 5179
Reputation: 7281
You can use the Ruby 1.8.7 gem "crontab-parser" to parse a crontab, and then iterate through each minute between the two timestamps, calling should_run?
on every crontab entry:
require 'rubygems'
require 'crontab-parser'
start_time = Time.parse(ARGV[0])
end_time = Time.parse(ARGV[1])
# CrontabParser barfs on env variable setting in a crontab, so just skip them
cron_data = File.readlines(ARGV[2]).select {|line| line =~ /^[0-9*]/}
cron = CrontabParser.new(cron_data.join("\n"))
(start_time..end_time).each do |timestamp|
next unless timestamp.sec.to_i == 0
cron.each do |cron_entry|
if cron_entry.should_run?(timestamp)
puts "#{timestamp}\t#{cron_entry.cmd}"
end
end
end
So, if you have a crontab.txt
that looks like this:
* * * * * foo
18 15 * * * bar
Then call the script with output from date
or similar and a file containing a crontab:
ruby cron_report.rb "Sat Mar 3 02:07:32 UTC 2012" "Sat Mar 3 02:21:32 UTC 2012" crontab.txt
You'll get output like this:
Sat Mar 03 15:17:00 UTC 2012 * * * * * foo
Sat Mar 03 15:18:00 UTC 2012 * * * * * foo
Sat Mar 03 15:18:00 UTC 2012 18 15 * * * bar
Sat Mar 03 15:19:00 UTC 2012 * * * * * foo
Sat Mar 03 15:20:00 UTC 2012 * * * * * foo
Upvotes: 2
Reputation: 11473
Since this doesn't seem possible without parsing cron, I decided to write it myself in perl:
(not sure why the formatting is fubar)
#!/usr/bin/perl -w
use strict;
use Set::CrossProduct;
my $begin;
my $end;
if($ARGV[0] && $ARGV[0] =~ /before/i){
$begin = 0;
$end = $ARGV[1];
}
elsif($ARGV[0] && $ARGV[0] =~ /after/i){
$end = 2400;
$begin = $ARGV[1];
}
else{
$begin = $ARGV[0];
$end = $ARGV[1];
}
if(!defined($begin) || !defined($end)){
print STDERR "Invalid Arguments\n";
exit 1;
}
my @crontab = `crontab -l`;
foreach my $cronjob (@crontab){
chomp $cronjob;
next if $cronjob =~ /^ *\#/ ||$cronjob =~ /^ *$/ ;
#print "in: $cronjob\n";
my ($min,$hour,$day_of_month,$month,$day_of_week, @cmd) = split(/ /, $cronjob);
my @mins = expandRange($min,0,59);
my @hours = expandRange($hour,0,23);
my $cp = Set::CrossProduct->new([\@hours,\@mins]);
my $combos = $cp->combinations();
foreach my $time ( map { $_->[0]*100 + $_->[1] } @$combos){
if($time >= $begin && $time <= $end){
print $cronjob,"\n";
last; #don't print the job n times, just once
}
}
}
sub expandRange{
my ($in,$begin,$end) = @_;
#print "in: ($in)[$begin:$end]\n";
my @range;
my @vals = split(/,/,$in);
foreach my $val (@vals){
my $mult = 1;
if($val =~ /\/(.+)$/){
$mult = $1;
$val =~ s/\/(.+)//;
}
if($in =~ /\*/){
@range = grep { $_ % $mult == 0 && $_ >= $begin && $_ <= $end } $begin..$end;
}
elsif($val =~ /[\-:]/){
my ($first, $last) = split(/[\-:]/,$val);
push(@range, grep { $_ % $mult == 0 && $_ >= $begin && $_ <= $end } $first..$last);
}
elsif($val >= $begin && $val <= $end) {
push(@range, $val);
}
}
my %unique;
@unique{@range} = 1;
return sort keys %unique;
}
Upvotes: 2
Reputation: 26086
Anything is possible but you will have to parse crontab yourself.
There are no simple answers, but just because I can here's a partial solution in bash.
#!/bin/bash
start="${1-0:0}"
end="${2-0:0}"
start_hour=$(cut -d: -f1 <<<"$start")
end_hour=$(cut -d: -f1 <<<"$end")
start_min=$(cut -d: -f2 <<<"$start")
end_min=$(cut -d: -f2 <<<"$end")
# leading zeroes would be bad
let start_hour=10#${start_hour}
let end_hour=10#${end_hour}
let start_min=10#${start_min}
let end_min=10#${end_min}
cat /etc/crontab | \
grep -v ^\# | \
grep -E -v '^([a-zA-Z]+)' | \
awk '{print $1, $2, $7}' | \
while read line ; do
if [ ! -z "$line" ] ; then
h=$(cut -d' ' -f2 <<<"$line")
m=$(cut -d' ' -f1 <<<"$line")
cmd=$(cut -d' ' -f3- <<<"$line")
if [ "$h" = '*' ] || ( [ $h -ge $start_hour ] && [ $h -le $end_hour ] ) ; then
if [ "$m" = '*' ] || ( [ $m -ge $start_min ] && [ $m -le $end_min ] ) ; then
echo $cmd
fi
fi
fi
done
Call like
cron_between 09:00 16:59
This certainly won't work for complex time specifications (e.g. */2) and only reports on the first part of the command. All of this can be corrected, but probably you'd be better off doing it in perl or something.
Upvotes: 4
Reputation: 7465
You could copy and paste the crontab file into Excel and then create a few functions that do this. Since the time fields are always in the same place, your columns in Excel would correspond to the timeframe in which that command is executed.
You'll have to write your formula so that is accounts for single integer values and repeating values (like 10 versus */10).
If your crobtab file changes a lot and you have many entries, then you could probably write a php script to parse this information pretty quickly.
Upvotes: 1