I have been blowing my mind over few days to do this.. (Don't ask me why I want to do it!)
How about adding two jobs like this?
00 06 current_day-31/3 * * job
00 18 next_day-31/3 * * job
I have been blowing my mind over few days to do this.. (Don't ask me why I want to do it!)
How about adding two jobs like this?
00 06 current_day-31/3 * * job
00 18 next_day-31/3 * * job
As @Rinzwind says, you should run a script that should check if it's time to run, every 12 hours
0 0,6,12,18 * * * /path/to/script.sh
The catch is that if you divide by 36 directly, it should have to run at 00 and 12 exactly, also it will skip the first day of the year.
This script allows choosing arbitrary values for ~00 and ~36 hours
#!/bin/bash
# Global variable with result from shouldItRun()
runCommand=0
# Run at this hours
runAt00=6
runAt36=18
# There's an issue when the year changes. Since ((365*24)/36) is
# not exact, so using the cron-only solution @JEL, it will run at
# 6PM on day 365 of first year, and at 6am on day 1 of second year
# So ...
# Year when the script started running
yearStart=2015
# Get number of days since the first year the script is running
yearCurrent=$((`date '+%Y'`-1))
daysAccumulatedUntilThisYear=0
for i in $(seq $yearStart $yearCurrent)
do
thisYearDays=$((`date -d ''$i'-12-31' '+%j'`+0))
daysAccumulatedUntilThisYear=$(($daysAccumulatedUntilThisYear+$thisYearDays));
echo '>> Year '$i' ('$thisYearDays') | Accumulated : '$daysAccumulatedUntilThisYear
done
function shouldItRun {
# Init to false
runCommand=0
#
hourOfMonth=$(($(($daysAccumulatedUntilThisYear+$1+0))*24))
hourOfDay=$(($2+0))
# POSIBLE VALUES 24,12,0
hoursLeft=$(( $hourOfMonth % 36 ))
# IF 24 hours left AND time = $runAt00:XX
if [ $hoursLeft -eq 24 ] && [ $hourOfDay -eq $runAt00 ]
then
echo '>>> DAY '$1' '$2':XX ('$hourOfMonth') : '$hoursLeft
runCommand=1
fi
# IF 12 hours left AND time = $runAt36:XX
if [ $hoursLeft -eq 12 ] && [ $hourOfDay -eq $runAt36 ]
then
echo '>>> DAY '$1' '$2':XX ('$hourOfMonth') : '$hoursLeft
runCommand=1
fi
}
# Example of today at this time
dayOfYear=`date '+%j'`
hourOfDay=`date '+%H'`
#echo '> '$dayOfYear
#echo '> '$hourOfDay
shouldItRun $dayOfYear $hourOfDay
if [ $runCommand -eq '1' ]
then
echo 'Run it now!!'
else
echo 'STOP : do not run it now!!'
fi
# Example with first 20 days of year, at 06: and 18:,
# but tested also at 00: and 12:
for i in {1..20}
do
shouldItRun $i 00
shouldItRun $i 06
shouldItRun $i 12
shouldItRun $i 18
done
Use two crontab lines. Date utility output gets the day of the year (date +\%j) and bc output assigns a 0, 1 or 2 to the day with remainder arithmetic (the modulo operator). Although the configuration can be any 36 hour spread, one example configuration is to run the job at 6am on the 1 days, at 6pm the day after, which will be a 2 day, and on the 0 days do not run the job.
0 6 * * * jd=`/bin/date +\%j`; /usr/bin/test `/bin/echo "$jd"\%3 | /usr/bin/bc` -eq 1 && job
0 18 * * * jd=`/bin/date +\%j`; /usr/bin/test `/bin/echo "$jd"\%3 | /usr/bin/bc` -eq 2 && job
You can test this on the command line with something like
jd=`/bin/date +\%j`; /usr/bin/test `/bin/echo "$jd"%3 | /usr/bin/bc` -eq 2 && echo 'yes'
Replace jd with a target day of the year, and the 2 in "-eq 2" with 0 or 1 to see the results for a given day. For example, in 2016, days of the year 1-3 and 364-366:
1%3 = 1 364%3 = 1
2%3 = 2 365%3 = 2
3%3 = 0 366%3 = 0
So, using cron, the date and test utilities, and bc as shown in the example above, the job will run at 6am on days 1 and 364 of 2016, again at 6pm on days 2 and 365, and will not run on days 3 and 366.
A generous commenter and another answer brought to light that the above answer fails for every year except a leap year (2016, 2020). This is because the remainder pattern fails to hold across the year boundary following non-leap years (365 day years). Therefore the remainder pattern needs to be coerced into a 4-year pattern, rather than a one year pattern.
The much uglier command line test for the 4-year pattern is
jd=`/bin/date +\%j`;ya=`/bin/date +\%Y`;/usr/bin/test `/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc` -eq 0 && /bin/echo 'yes'
which, if the job is to be run every 36 hours (for example, as shown, at 6am one day, 6pm the next day, then 6am the day after the next day) could be run with two crontab lines as shown in the single calendar year example, that is
0 6 * * * jd=`/bin/date +\%j`;ya=`/bin/date +\%Y`;/usr/bin/test `/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc` -eq 1 && job_to_run
0 18 * * * jd=`/bin/date +\%j`;ya=`/bin/date +\%Y`;/usr/bin/test `/bin/echo if \("ya"%4\) 4-"ya"%4+"jd"%3 else "jd"%3 | /usr/bin/bc` -eq 2 && job_to_run
Two caveats deserve mention:
I assume familiarity with the use of crontab fields (otherwise there's always the man page). The two lines shown will run "job" at 6am and 6pm if the test expression given after the five time and date fields succeeds. The test expression succeeds as follows.
I'll explain the more complicated test expression, because the simpler expression is part of the more complicated expression.
jd=`/bin/date +\%j`;ya=`/bin/date +\%Y`
Next the output of a bc equation is tested (with /usr/bin/test). If the result of the bc calculation equals 1 ("-eq 1"), the job is run at 6am. If the result of the bc calculation equals 2, the job is run at 6pm. If the result of the bc calculation equals any other number including 0, the job is not run.
Then the echo utility is used to pipe the bc equation into the bc utility.
First in the bc equation is an "if" statement. If the result of bc evaluating the equation in parens after the "if", ("ya"%4)
, is nonzero, the next equation, 4-"ya"%4+"jd"%3
, is evaluated; otherwise, the equation after "else", "jd"%3
, is evaluated.
In all cases, "ya" is replaced with the current year number, and "jd" is replaced with the current day number.
For the "if" condition, the result is the remainder after dividing the year by 4. For 2016, the remainder is 0, so the "else" equation is calculated. For 2017, the remainder is 1, for 2018, the remainder is 2, and for 2019, the remainder is 3. So, for 2017-2019 (but not for 2016 or 2020), the equation 4-"ya"%4+"jd"%3
is calculated. In the case of 2017, the result of 4-2017%4 is 3 (4-1); in 2018, the result is 2, and in 2019, the result is 1. That result is added to the day number before dividing the day number by 3 to get the remainder used to test whether or not the job should run.
Patterns of 1,2,0 resulting from the tested bc calculation for days 1-3 and 363-365 in 2017-2019 are shown here:
2017 2018 2019 3 + 1 % 3 = 1 2 + 1 % 3 = 0 1 + 1 % 3 = 2 3 + 2 % 3 = 2 2 + 2 % 3 = 1 1 + 2 % 3 = 0 3 + 3 % 3 = 0 2 + 3 % 3 = 2 1 + 3 % 3 = 1 3 + 363 % 3 = 0 2 + 363 % 3 = 2 1 + 363 % 3 = 1 3 + 364 % 3 = 1 2 + 364 % 3 = 0 1 + 364 % 3 = 2 3 + 365 % 3 = 2 2 + 365 % 3 = 1 1 + 365 % 3 = 0