9

In crontab, i have seen a couple of answers where users have requested a way to run every 5 mins:

*/5 * * * * command

Or every 5 mins with an offset:

10-59/5 * * * * command

An i have seen some creative solutions where people have made 2 line crontabs for every 90 mins:

0 0-21/3 * * * whatever
30 1-22/3 * * * whatever

Thanks to one of the answers below (thanks @khaled), here is another creative solution to 3h30mins:

0 0-23/7 * * * whatever
30 3-23/7 * * * whatever

Is there a generic solution to the every x mins problem? For example I currently need every 3h30m.

To address the duplicate tag: The question linked is clearly different - there is general advice on how to setup and debug cron jobs. I am referring to a specific problem where we are reaching the limits of the cron syntax. I am looking for a general algorithm for solving the "every greater than 60 mins" problem, which is not addressed in the linked question.

user230910
  • 205
  • 2
  • 8
  • 2
    @JennyD and where in that question/answer does it describe the mathematical strategy behind setting up intervals not directly supported by the configuration? Having a canonical question about a command is not justification for blindly closing questions about usage not covered by that post. – Kevin Jun 20 '17 at 19:59
  • @Kevin That would be fairly far down on the page, in [the answer titled "Uncommon and irregular schedules"](https://serverfault.com/a/815541/120438). HTH, HAND. – Jenny D Jun 21 '17 at 05:24

3 Answers3

10

You just need to do the math yourself to come up with the required jobs that to achieve your timing.

0 0-23/7 * * * whatever
30 3-23/7 * * * whatever

A simple algorithm can be concluded from these two examples (when having 30 minutes offset):

  • Add two entries: one with 0 minutes offset and another with 30 minutes offset.
  • Specify hours range width equals to duration * 2.
  • Specify hours offset: one starts with 0, and the other starts with duration after discarding 30-minute part.

If you think more, you can come up with similar solutions for something like every 75 minutes.

Edit:

Cron can not be used for all types of scheduled jobs. For example, executing a job once per month on the last day of month. You can not simply do it with cron because the last day of month changes from month to another. To solve this, you can run a cron job in the possible range of values of last day of month (28-31) and verify it is really the last day or not in the script before doing the actual job.

Khaled
  • 35,688
  • 8
  • 69
  • 98
  • doing the math manually leads me to the same answer as yours given above for 3h30 mins (except it runs 30mins too early on the next day) – user230910 Jun 20 '17 at 08:29
  • The algorithm you are building is actually what I'm looking for, a way to make crontab work for any x number of mins, that i can bookmark and refer to in future – user230910 Jun 20 '17 at 08:30
  • Surely someone cleverer than both of us has already done this work? – user230910 Jun 20 '17 at 08:30
  • @user230910: You can not avoid such thing (30mins earlier execution), because this is how cron timing is calculated. – Khaled Jun 20 '17 at 08:31
  • well i guess i can limit it, because i can specify an even days and odd days rule – user230910 Jun 20 '17 at 08:32
  • but what i actually mean is that with cron being around for so long, surely someone has already solved the general case of this problem? – user230910 Jun 20 '17 at 08:33
  • 1
    @user230910 The solution for the general case is to not use cron for things it doesn't do well. – Jenny D Jun 20 '17 at 08:40
  • 1
    There is no sensible generic way of doing it in cron; if the time span isn't a whole fraction of a day, there isn't a way of doing it. For example, you might decide to create a line for every exception, as per examples above. If you apply this logic to a time span of 23:59, you would have to create at least 1440 lines (24hr*60min/hr) – Pak Jun 20 '17 at 08:42
  • @pak it clearly handles "every 90 mins" well, and im sure that there are a lot of other situations that scale nicely. But i take your point.. – user230910 Jun 20 '17 at 08:45
  • 2
    I'd also suggest considering [systemd timers](https://www.freedesktop.org/software/systemd/man/systemd.timer.html), which can serve as a [cron replacement](https://wiki.archlinux.org/index.php/Systemd/Timers) while being far more flexible (and, yes, it [can do last day of month](https://unix.stackexchange.com/questions/306336/how-to-configure-systemd-timer-to-run-the-service-on-last-day-of-the-month), though only very recent versions as of 2017). – Bob Jun 20 '17 at 11:39
6

Put the result of the command

date +%s

in a variable in your crontab. Something like TIME=1497950105. Now in your crontab you need an entry like

* * * * * /bin/bash -c '[[ $(($(date +\%s)-TIME)) -gt seconds ]] && TIME=$(date +\%s) && whatever'

Where seconds is the number of seconds you want (12600 in your case ).

Or if you want to wait 3 hours and 30 minutes after the completion of the program

* * * * * /bin/bash -c '[[ $(($(date +\%s)-TIME)) -gt seconds ]] && whatever && TIME=$(date +\%s)'

Edit: I corrected my prevoius answer:

  • you have to escape % sign with \, so %s becomes \%s
  • you have to precede the command with /bin/bash -c

Another solution (without using TIME) is:

 * * * * * /bin/bash -c '[[ $((($(date +\%s) / 60) % minutes)) -eq 0 ]] && whatever'

Where minutes is 210 in your case.

Edit 2:

As suggested by MSalters it's better to run the entry every N minutes, where N is the greatest common divisor between 60 and your time interval in minutes

5

In this case I think you're simply trying to use the cron to do something it is not capable of doing by itself (of course excepting the solutions where cron actually executes a helper script periodically, in which case I'd argue it's not cron itself solving the problem).

The solution with the 3 hour 30 minute interval is not entirely correct. This was the given solution:

0 0-23/7 * * * whatever
30 3-23/7 * * * whatever

The reason this is wrong is because cron will then run the job at the times 21:00 and 00:00, which is an interval of 3 hours rather than 3 hours 30 minutes.

A general solution for all time intervals would have to be able to handle cases that do not evenly divide into 24 hours. While it does not divide evenly into 24 hours, it does divide evenly into a week! The easiest way of thinking of this is to do two overlapping sets of "every 7 hours", like this:

0 0-23/7 * * 1 whatever
0 4-23/7 * * 2 whatever
0 1-23/7 * * 3 whatever
0 5-23/7 * * 4 whatever
0 2-23/7 * * 5 whatever
0 6-23/7 * * 6 whatever
0 3-23/7 * * 7 whatever
30 3-23/7 * * 1 whatever
30 0-23/7 * * 2 whatever
30 4-23/7 * * 3 whatever
30 1-23/7 * * 4 whatever
30 5-23/7 * * 5 whatever
30 2-23/7 * * 6 whatever
30 6-23/7 * * 7 whatever

Something being divisible by a week is the best you can do, because months do not line up perfectly with weeks, and that's the only way it's doable. For example, an interval of precisely every 12 minutes is trivial in one line of crontab, but an interval of precisely every 11 minutes is impossible, because 10080 is not divisible by 11. (10080 being the number of minutes in a week).

It would definitely be possible to write an algorithm to solve the cases where this is possible, but it's clearly not worth very much, especially considering what the solutions would actually look like.

Per von Zweigbergk
  • 2,615
  • 2
  • 17
  • 27
  • thanks so much for the effort you put into this - it makes sense to me. – user230910 Jun 21 '17 at 10:05
  • it seems that it is possible to come up with an algortihm to calculate lists of crontab lines that work nicely in divide into hour/day/week and even though this is a 14 line monster, its still trivial if it was generated by a tool – user230910 Jun 21 '17 at 10:06