Perhaps the easiest way is to simply complete three separate tasks:
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
This will work on February 28th, although even in leap years, if this is a problem, you need to find another way.
However, as a rule, itβs both much easier and the right way to start work as soon as possible on the first day of each month, with something like:
0 0 1 * * myjob.sh
and change the script to process the data of the previous month.
This eliminates any difficulties that you may encounter when it turns out which day is the last month, and also ensures the availability of all data for that month if you are working with data. Five minutes before midnight on the last day of the month, you can see that you are missing something that happens between noon and noon.
This is the usual way to do this anyway for most tasks at the end of the month.
If you still want to run it on the last day of the month, one of them is simply to determine whether tomorrow will be the first (either as part of your script or crontab itself).
So something like:
55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh
should be a good start if you have a relatively smart date program.
If your date program is not advanced enough to give you relative dates, you can simply put together a very simple program to give you the day of the month tomorrow (you don't need the full date power), for example:
#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 named it tomdom for "tomorrow of the month"):
55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh
Although you might want to add error checking, since time() and mktime() can return -1 if something goes wrong. The above code, for reasons of simplicity, does not take this into account.