27

Is it possible to offset a cron script set to run every 5 minutes?

I have two scripts, script 1 collects some data from one database and inserts it into another, script 2 pulls out this data and a lot of other data and creates some pretty reports from it. Both scripts need to run every 5 minutes. I want to offset script 2 by one minute so that it can create a report from the new data. E.G. I want script one to run at :00, :05, :10, :15 [...] and script two to run at :01, :06, :11, :16 [...] every hour. The scripts are not dependent on each other, and script 2 must run regardless of whether script one was successful or not. But it would be useful if the reports could have hte latest data. Is this possible with cron?

Post;
I have thought about using both commands in a shell script so they run immediately after each other but this wouldn't work, sometimes script 1 can get hung up on waiting for external APIs etc. so might take up to 15 mins to run, but script 2 must run every 5 minutes so doing it this way would stop/delay the execution of script 2. If I could set this in Cron it would mean script 2 would run regardless of what script 1 was doing

5 Answers 5

13

You can run scripts whenever you want using cron. If you want to run script 1 every 5 minutes, you might start like this:

*/5 * * * * /path/to/script1

But this is really just shorthand for:

0,5,10,15,20,25,30,35,40,45,50,55 * * * * /path/to/script1

If you want to run script 2 one minute after script 1, you can do this:

1,6,11,16,21,26,31,36,41,46,51,56 * * * * /path/to/script2

You could also do this:

*/5 * * * * /path/to/script1
*/5 * * * * /path/to/script2

And then at the start of script 2, sleep for one minute:

sleep 60
1
  • 8
    Putting the sleep in the script itself is an ugly hack that makes running the script by hand from the prompt painful. There is a less ugly way to hack that by making the sleep part of the crontab entry, but an even more elegant way is to just offset the jobs using the correct crontab notation, which your answer doesn't touch on.
    – Caleb
    Sep 18, 2012 at 9:42
54

The minute entry field for crontab accepts an "increments of" operator that is kind of confusing because it looks like it should be a mathematical "divide by" operator but isn't. You will most often see it used something like the following. Note that this does not find numbers that are divisible by five but rather takes every fifth item from a set:

 */5 * * * * command

This tells cron to match every fifth item (/5) from the set of minutes 0-59 (*) but you can change the set like this:

 1-59/5 * * * * command

This would take every fifth item from the set 1-59, running your command at minutes 6, 11, 16, etc.

If you need more fine grained offsets than one minute, you can hack it using the sleep command as part of your crontab like this:

 */5 * * * * sleep 15 && command

This would run your job every five minutes, but the command would not actually start until 15 seconds after the minute. For short running jobs where being a few seconds after something else makes all the difference but you don't want to be a full minute late, this is a simple enough hack.

1
  • 4
    This is a better answer than the accepted one. Setting the offset via the range is much cleaner than sleeping.
    – ND Geek
    May 18, 2018 at 15:23
13

You can indicate a time offset with the + symbol. For example, to run at :01, :06, :11, :16 [...], create a task such as

*/5+1 * * * * command
3
  • 8
    This doesn't seem to work in some versions of cron
    – jhoff
    May 2, 2013 at 15:53
  • 2
    Which version of cron are you using that supports this syntax? Cronie doesn't seem to support it.
    – Matthew G
    Mar 7, 2015 at 2:56
  • I have Cronie on CentOS7, so ty I won't try this solution first. Nov 28, 2017 at 15:06
2

This worked for me:

1/5 * ? * * *

Where 1 is the offset minutes. So if you want to offset three minutes:

3/5 * ? * * *

I have this working in AWS schedule settings

1
  • Unfortunately it doesn't work with Ubuntu 21.10 bad minute errors in crontab file, can't install. Jul 1, 2022 at 13:55
1

This is an XY problem. Offsetting cron from whole hour intervals, or common fractions thereof, can be generally valuable to reduce coinciding processes, but this question explicitly states the offset is to resolve a dependency. Though we're told script 2 must run regardless of whether script 1 completes, it should certainly not try whilst script 1 is in the middle of updating the shared dataset. Using time alone to enforce a sequence creates an unnecessary delay between the two at best, and a race condition at worst.

It's better to put them into the same cron job line to create the chronological dependency:

*/5 * * * * /path/to/script1; /path/to/script2

The semicolon separates the commands but ensures all will run regardless of the previous exit status.

In cases where the second command should run only if the first command is successful, change it to:

*/5 * * * * /path/to/script1 && /path/to/script2

I believe this is a case of the latter, where the report will not change if it's generated from old data, or will be plain wrong if generated from incomplete data, so why create another until a successful run of the first script? Either resend a cached copy of the previous report or none at all. If the first script has any chance of running longer than the cron interval, it should employ basic locking (e.g. with flock) to ensure there won't be concurrent runs making competing requests to the same places.

You must log in to answer this question.