I need to create a CRON job that will run on the last day of every month. I will create it using cPanel.
Any help is appreciated. Thanks
Possibly the easiest way is to simply do three separate jobs:
55 23 30 4,6,9,11 * myjob.sh
55 23 31 1,3,5,7,8,10,12 * myjob.sh
55 23 28 2 * myjob.sh
That will run on the 28th of February though, even on leap years so, if that's a problem, you'll need to find another way.
However, it's usually both substantially easier and correct to run the job as soon as possible on the first day of each month, with something like:
0 0 1 * * myjob.sh
and modify the script to process the previous month's data.
This removes any hassles you may encounter with figuring out which day is the last of the month, and also ensures that all data for that month is available, assuming you're processing data. Running at five minutes to midnight on the last day of the month may see you missing anything that happens between then and midnight.
This is the usual way to do it anyway, for most end-of-month jobs.
If you still really want to run it on the last day of the month, one option is to simply detect if tomorrow is the first (either as part of your script, or in the crontab itself).
So, something like:
55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh
should be a good start, assuming you have a relatively intelligent date program.
If your date program isn't quite advanced enough to give you relative dates, you can just put together a very simple program to give you tomorrow's day of the month (you don't need the full power of date), such as:
#include <stdio.h>
#include <time.h>
int main (void) {
// Get today, somewhere around midday (no DST issues).
time_t noonish = time (0);
struct tm *localtm = localtime (&noonish);
localtm->tm_hour = 12;
// Add one day (86,400 seconds).
noonish = mktime (localtm) + 86400;
localtm = localtime (&noonish);
// Output just day of month.
printf ("%d\n", localtm->tm_mday);
return 0;
}
and then use (assuming you've called it tomdom for "tomorrow's day of month"):
55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh
Though you may want to consider adding error checking since both time() and mktime() can return -1 if something goes wrong. The code above, for reasons of simplicity, does not take that into account.
There's a slightly shorter method that can be used similar to one of the ones above. That is:
[ $(date -d +1day +%d) -eq 1 ] && echo "last day of month"
Also, the crontab entry could be update to only check on the 28th to 31st as it's pointless running it the other days of the month. Which would give you:
0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh
You could set up a cron job to run on every day of the month, and have it run a shell script like the following. This script works out whether tomorrow's day number is less than today's (i.e. if tomorrow is a new month), and then does whatever you want.
TODAY=`date +%d`
TOMORROW=`date +%d -d "1 day"`
# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
echo "This is the last day of the month"
# Do stuff...
fi
Some cron implementations support the "L" flag to represent the last day of the month.
If you're lucky to be using one of those implementations, it's as simple as:
0 55 23 L * ?
That will run at 11:55 pm on the last day of every month.
http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger
#########################################################
# Memory Aid
# environment HOME=$HOME SHELL=$SHELL LOGNAME=$LOGNAME PATH=$PATH
#########################################################
#
# string meaning
# ------ -------
# @reboot Run once, at startup.
# @yearly Run once a year, "0 0 1 1 *".
# @annually (same as @yearly)
# @monthly Run once a month, "0 0 1 * *".
# @weekly Run once a week, "0 0 * * 0".
# @daily Run once a day, "0 0 * * *".
# @midnight (same as @daily)
# @hourly Run once an hour, "0 * * * *".
#mm hh Mday Mon Dow CMD # minute, hour, month-day month DayofW CMD
#........................................Minute of the hour
#| .................................Hour in the day (0..23)
#| | .........................Day of month, 1..31 (mon,tue,wed)
#| | | .................Month (1.12) Jan, Feb.. Dec
#| | | | ........day of the week 0-6 7==0
#| | | | | |command to be executed
#V V V V V V
* * 28-31 * * [ `date -d +'1 day' +\%d` -eq 1 ] && echo "Tomorrow is the first today now is `date`" >> ~/message
1 0 1 * * rm -f ~/message
* * 28-31 * * [ `date -d +'1 day' +\%d` -eq 1 ] && echo "HOME=$HOME LOGNAME=$LOGNAME SHELL = $SHELL PATH=$PATH"
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/{ print $NF }' | tail -1) ]] && job
Check out a related question on the unix.com forum.
You can just connect all answers in one cron line and use only date command.
Just check the difference between day of the month which is today and will be tomorrow:
0 23 * * * root [ $(expr $(date +\%d -d '1 days') - $(date +\%d) ) -le 0 ] && echo true
If these difference is below 0 it means that we change the month and there is last day of the month.
The last day of month can be 28-31 depending on what month it is (Feb, March etc). However in either of these cases, the next day is always 1st of next month. So we can use that to make sure we run some job always on the last day of a month using the code below:
0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh