Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.
*/30 */30 * * * python /root/get_top.py

I'm trying to run a script every 30 hours and 30 minutes. Is the syntax above proper for that?

I've had multiple people tell me that */30 is not a valid value for the hour column, since it's greater than 24. If that's true, how do I make a cron for a job that should run every 30 hours?

share|improve this question

3 Answers 3

up vote 4 down vote accepted

The simplest solution would probably be to run a cronjob more frequently and use a wrapper script to quit without doing anything if not enough time has passed.

To figure out how often you need to run, take the greatest common factor of cron's limits and your desired interval.

So, for "every 30 hours, 30 minutes", that'd be "every 30 minutes" and, for "every 30 hours", that'd be "every 6 hours" (The greatest common factor of 30 and 24)

You can implement the wrapper one of two ways:

First, you could store a timestamp in a file and then check if the time difference between now and the stored timestamp is greater than or equal to 30 hours and 30 minutes.

This seems simple enough, but has two potential gotchas that complicate the code:

  1. Failsafe parsing of the saved timestamp file
  2. Allowing for some wiggle forward and back when comparing timestamps since other things happening on the system will cause the actual interval to wiggle around.

The second option is to not store a timestamp file at all and, instead, do some math. This is also theoretically faster since the kernel can return the system time without querying the hard drive.

I haven't tested this for typos, but here's Python code for it that's been expanded out for clarity.

import os, time

full_interval = 1830 # (30 hours * 60 minutes) + 30 minutes
cron_interval = 30   #  30 minutes

minutes_since_epoch = time.time() // 60

allowed_back_skew = (cron_interval * 0.1)
sorta_delta = (minutes_since_epoch + allowed_back_skew) % full_interval

if sorta_delta < cron_interval:
    os.execlp('python', 'python', '/root/get_top.py')

Here's the idea behind it:

  1. Just as "a stopped clock is right twice a day", the value of minutes_since_epoch % full_interval will only be less than cron_interval once per full_interval.
  2. We need to fuzzy-match to account for variations caused by sharing resources with other processes.
  3. The easiest way to do this is to use [0, cron_interval) as a window within which a task must fall in order to be executed.
  4. To account for jitter in both directions, we slide the starting edge of the window back by 10% of its duration since running too early will be rare while running too late can happen any time the system is so bogged down that the wrapper script is delayed in calling time.time().

If, as I suspect, get_top.py is your own creation, just stick this at the top of it and change the check to

if sorta_delta > cron_interval:
    sys.exit(0)
share|improve this answer
    
This is a pretty reasonable solution. –  bahamat Mar 11 at 4:20

Multiple people are correct. */30 is not valid in the hour column, because (in any column), */n means "every whatever which is divisible by n". That's usually explained as "every n whatevers", but that's not actually correct; for example, if you put */5 in the hours column, the script will run at 0:00, 5:00, 10:00, 15:00 and 20:00.

Also, the columns are independent of each other. If you put */10 in the hours column and 2,17 in the minutes column, the script will run at 0:02, 0:17, 10:02, 10:17, 20:02 and 20:17.

I don't know of an easy way to run a script every 1830 minutes. You could use the script to reschedule itself in 1830 minutes using the at command, but that's not very robust.

share|improve this answer

Here's a method for doing what you want with the caveat that it will run every 30 hours from Unix's epoch time.

0 * * * * [ $(( $(date +%s) / 360 % 30 )) -eq 0 ] && your_command

This will run every hour, performing a calculation to determine if this hour is divisible by 30. If it is the modulo division (% 30) will return a 0 thus signifying that we're in a 30th hour situation. If this is the case then your command (your_command) will execute.

As others have mentioned you cannot do what you want using the */30 since those values exceed the value of that particular time field in cron.

You can adjust the above example further if you need to skew it 30 minutes either by adjusting the cron (for example using 30 instead of 0) or by adding 30 to the conditional block.

share|improve this answer
    
Keep in mind that in some (many?) implementations of cron, % needs to be escaped as %%. It has a special behavior to pass the string following % as stdin. –  cpugeniusmv Mar 12 at 13:31
    
@cpugeniusmv - Good to know, thanks for the info. –  slm Mar 12 at 14:14

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.