EyeOpener has asked for the wisdom of the Perl Monks concerning the following question:

I have a script that I'd like to run every two hours on the half-hour (i.e. at 3:30, 5:30, etc.). Doing this with NT's AT scheduler (and Unix's crontab, I think) would require 12 separate scheduling lines. That's not fun to administer if the schedule changes. I suppose I could find a more robust scheduler, but I'm curious if there's a Perlish solution my fellow monks like to employ in situations like this.

I started working on a simple sub to include in my script, which would sleep for a while, check the time, then come to life and run the main part of the script. I could fudge by with that in this case, but the script won't really start "on time", and this solution is not all that elegant.

What I'm imagining is some bit of code that wakes the script up at the right time, each time. I haven't tried a loop of code that continually checks the time without sleeping, because that seems like it would be extremely processor-intensive. I suppose this is exactly what schedulers are for, but is there a different/better solution?

Thanks!

Replies are listed 'Best First'.
Re: Scheduling with Perl?
by derby (Abbot) on Feb 04, 2003 at 22:32 UTC
    One crontab entry:

    30 1,3,5,7,9,11,13,15,17,19,21,23 * * * echo "run every odd half hour"

    but I agree, 0-23/2 would look nicer in the crontab

    -derby

      or:
      30 * * * * echo "run every half hour"
      And then verify that the hour is not % 2 to run every half hour in the script.
      Psudocode: if (! $currenthour % 2) { dostuff; } else { exit 0; #oopsie it is an even hour just exit }


      -Waswas
      I can do one better :

      30 0-23/2 * * * foo

      -Matt
Re: Scheduling with Perl?
by data64 (Chaplain) on Feb 04, 2003 at 23:39 UTC

    I not sure what platform you are on and whether you are looking for a platform independent solution.

    I guess for your approach, having an alarm signal for every two hours might make more sense. But I have never tried using alarm for more than 5 minutes and am not even sure if it is supported on Win32.

    BTW, this functionality is pretty straightforward to do with crontab in one line. eg: 30 1-23/2 * * * runs it every two hours, 30 min past the hour (at least it does on Linux)

    For information about NT At see Schedule Win32 Perl Scripts with AT. I know that you can schedule things like that from GUI, never tried it with the API. For Windows there is also Win32::Scheduler available from http://www.roth.net/perl/packages/.

    You should also look through the offerings on CPAN, eg: Schedule::Cron does exactly what you are trying to do.


    Just a tongue-tied, twisted, earth-bound misfit. -- Pink Floyd

Re: Scheduling with Perl?
by Corion (Patriarch) on Feb 05, 2003 at 08:39 UTC

    I wonder why nobody has mentioned it yet, but there is Schedule::Cron, and a nonforking subclass, Schedule::Cron::Nofork (this one is written by me but planned to be included within Schedule::Cron in the next release as far as I gather).

    These modules allow you easy scheduling via a permanently running script, and then either forking or running a command, waiting for it to end.

    Some code I have running on my PC (under NT) :

    #!/usr/bin/perl -w use strict; use Schedule::Cron::Nofork; use File::Spec; use FindBin; use Logger qw(my.email@my-work.net); my $cron = Schedule::Cron::Nofork->new( sub { my $self = shift; warn "$$ Running $_[0]\n"; system("start \"Cron: $_[0]\" \"$_[0]\""); }); warn "Starting cron\n"; $cron->add_entry("0,5,10,15,20,25,30,35,40,45,50,55 18 * * 1-5", RUN = +> File::Spec->catfile( $FindBin::Bin, "transfer-OPICS-Vertex-53.pl" ) +); $cron->add_entry("0,5,10,15,20,25,30,35,40,45,50,55 18 * * 1-5", RUN = +> File::Spec->catfile( $FindBin::Bin, "transfer-OPICS-SapBCA-53.pl" ) +); $cron->add_entry("0,5,10,15,20,25,30,35,40,45,50,55 18 * * 1-5", RUN = +> File::Spec->catfile( $FindBin::Bin, "transfer-OPICS-SapFi-53.pl" )) +; #$cron->add_entry("* * * * 1-5", RUN => File::Spec->catfile( $FindBin: +:Bin,"test.pl")); $cron->run();

    I start the scripts via start, because I don't need and don't trust the built-in forking simulation - it's amazing that it works, but for a simple script that dosen't really need the resource sharing features, I avoid it.

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
Re: Scheduling with Perl?
by talexb (Chancellor) on Feb 04, 2003 at 22:16 UTC

    How about scheduling your script to run every two hours (I think that's with */2, to get 0000, 0200, 0400, etc.), then have the script start with a 90 minute sleep?

    --t. alex
    Life is short: get busy!
Re: Scheduling with Perl?
by dbp (Pilgrim) on Feb 04, 2003 at 22:31 UTC
    First of all, let me say that I think you are engagning in false hubris with this since 12 lines in cron or at isn't a very big deal. That said, I often obsess about elegant solutions myself, so what the hell. Any scheduler has to sleep between checks if you don't want to waste cycles. Yet very short sleeps fix this problem. In perl, sleeping for less than a second is somewhat difficult. If it is supported on NT use the 4-argument version of select():
    select(undef, undef, undef, 0.25);
    or use Time::HiRes:
    use Time::HiRes qw(sleep); sleep(0.25);
    You can also make your sleep time variable; have it sleep an hour if there are over two hours left, 30 minutes for an hour, etc. Once you hit a certain threshold, start using subsecond sleeps.
Re: Scheduling with Perl?
by Thelonius (Priest) on Feb 05, 2003 at 00:31 UTC
    use Time::Local; use strict; sub waityourturn { my $now = time; my $twohours = 60*60*2; my $onehour = 60*60; my ($sec,$min,$hour,$mday,$mon,$year) = localtime($twohours+$now); if ($hour % 2 == 0) { # if hour is even, let's just add one hour ($sec,$min,$hour,$mday,$mon,$year) = localtime($onehour+$now); } $sec = 0; $min = 30; my $target_time = timelocal($sec,$min,$hour,$mday,$mon,$year); my $naptime = $target_time - time; print "I am going to sleep $naptime seconds until ", scalar(localtime($target_time)), "\n"; sleep($naptime); }
Re: Scheduling with Perl?
by EyeOpener (Scribe) on Feb 06, 2003 at 18:43 UTC
    Thanks to all for the good suggestions. It's nice to learn the crontab is more flexible than I knew, but I am working with NT's AT in this case. Even worse ;-), it's NT4 with the basic scheduler, not the upgraded version with the control panel icon. It truly does require 12 lines for this schedule, and as some pointed out, there can be frustrating and insurmountable problems scheduling Perl scripts (or probably anything) with AT.

    I like the Schedule::Cron and Schedule::Cron::Nofork options. I did try to find modules on CPAN before posting this question, but I've never gotten the hang of finding what I need there. It's simple if I know the specific module I want, but I've never been able to discover a module on CPAN that solves an immediate need. I don't know if this is more indicative of an indexing/description problem on CPAN or of my own poor search methods, but at least I have the monks to point me in the right direction!

    Thanks again!
Re: Scheduling with Perl?
by perrin (Chancellor) on Feb 05, 2003 at 00:36 UTC
    As other have pointed out, cron can do this. It certainly looks to me like the Windows Scheduler can do it too. You just tell it to run every 2 hours starting at 12:30.
Re: Scheduling with Perl?
by Cabrion (Friar) on Feb 05, 2003 at 02:15 UTC
    You could always use perl to write the batch file for AT.
    open (BAT, ">setup.bat") || die $!; for (my $i = 2; $i <= 24; $i += 2) { print BAT "at $i:00 /every M T W Th F S Su somejob.bat\r\n"; }
    then just execute setup.bat!

    Lighthearted tone aside, I have used perl to generate everything from batch files to 3D Studio MAX scripts to perl modules.