Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find cron jobs that run between given times

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.

like image 949
frankc Avatar asked Oct 20 '25 09:10

frankc


2 Answers

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.

like image 60
sorpigal Avatar answered Oct 21 '25 22:10

sorpigal


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;

}

like image 32
frankc Avatar answered Oct 21 '25 21:10

frankc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!