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
What about this one, after Wikipedia?
55 23 L * * /full/path/to/command
For AWS Cloudwatch cron implementation (Scheduling Lambdas, etc..) this works:
55 23 L * ? *
Running at 11:55pm on the last day of each month.
Adapting paxdiablo's solution, I run on the 28th and 29th of February. The data from the 29th overwrites the 28th.
# min hr date month dow
55 23 31 1,3,5,7,8,10,12 * /path/monthly_copy_data.sh
55 23 30 4,6,9,11 * /path/monthly_copy_data.sh
55 23 28,29 2 * /path/monthly_copy_data.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
For a safer method in a crontab based on #Indie solution (use absolute path to date + $() does not works on all crontab systems):
0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh
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"
Set up a cron job to run on the first day of the month. Then change the system's clock to be one day ahead.
I found out solution (On the last day of the month) like below from this site.
0 0 0 L * ? *
CRON details:
Seconds Minutes Hours Day Of Month Month Day Of Week Year
0 0 0 L * ? *
To cross verify above expression, click here which gives output like below.
2021-12-31 Fri 00:00:00
2022-01-31 Mon 00:00:00
2022-02-28 Mon 00:00:00
2022-03-31 Thu 00:00:00
2022-04-30 Sat 00:00:00
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.
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash
What about this?
edit user's .bashprofile adding:
export LAST_DAY_OF_MONTH=$(cal | awk '!/^$/{ print $NF }' | tail -1)
Then add this entry to crontab:
mm hh * * 1-7 [[ $(date +'%d') -eq $LAST_DAY_OF_MONTH ]] && /absolutepath/myscript.sh
In tools like Jenkins, where usually there is no support for L nor tools similar to date, a cool trick might be setting up the timezone correctly. E.g. Pacific/Kiritimati is GMT+14:00, so if you're in Europe or in the US, this might do the trick.
TZ=Pacific/Kiritimati \n H 0 1 * *
Result: Would last have run at Saturday, April 30, 2022 10:54:53 AM GMT; would next run at Tuesday, May 31, 2022 10:54:53 AM GMT.
Use the below code to run cron on the last day of the month in PHP
$commands = '30 23 '.date('t').' '.date('n').' *';
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
Not sure of other languages but in javascript it is possible.
If you need your job to be completed before first day of month node-cron will allow you to set timezone - you have to set UTC+12:00 and if job is not too long most of the world will have results before start of their month.
If the day-of-the-month field could accept day zero that would very simply solve this problem. Eg. astronomers use day zero to express the last day of the previous month. So
00 08 00 * * /usr/local/sbin/backup
would do the job in simple and easy way.
Better way to schedule cron on every next month of 1st day
This will run the command foo at 12:00AM.
0 0 1 * * /usr/bin/foo
Be cautious with "yesterday", "today", "1day" in the 'date' program if running between midnight and 1am, because often those really mean "24 hours" which will be two days when daylight saving time change causes a 23 hour day. I use "date -d '1am -12 hour' "
Related
I have a requirement to insert new records and delete this record 3 months after created date.
I will create a new table.
I want that query will create the table as well as auto delete the records which are more than 3 months of the created date.
This sounds like a maintenance task - already suggested cron job would typically be the most suitable.
If you're free to use extensions - consider pg_cron:
CREATE EXTENSION pg_cron;
-- Delete old data daily at 3:30am (GMT)
SELECT cron.schedule(
'delete outdated records',
'30 3 * * *',
$$ DELETE FROM new_table WHERE created_date < now()-'3 months'::interval $$
);
Otherwise, save your script to a file and feed it to a regular cron job
# weekdays at 01:00am
# min hour mday month wday command-to-run
0 1 * * 1-5 psql -h dbhost -p 5432 -U dbuser --dbname dbname < my.sql
There's also the topic of how strict your rules are:
If the records can be 3 months old, +/- 1 day, the daily cron/pg_cron jobs will suffice. The lower your tolerance, the more often your cron will have to keep repeating the cleanup. At some point it might get impractical if it's effectively running all or most of the time. It could get to a point when next cleanup tasks start before previous ones finish, resulting in a growing queue.
If you absolutely can't afford to be in possession of records older than 3 months but it's tolerated when you already delete stuff that's one day away from the 3 month mark, you can change the cron job to delete records a day earlier
-('3 months'::interval-'1 day'::interval)
If you are allowed to be in possession of those records, just no longer allowed to show outdated records - hide the table behind a view that's filtering out everything older than exactly 3 months. Demo:
create table new_table (data text, created_date timestamp default now());
insert into new_table (data) values ('value1');
alter table new_table rename to new_table_raw;
create view new_table as
select * from new_table_raw where created_date>now()-'3 months'::interval;
--insert, update, delete still work even though it's a view now
insert into new_table (data) values ('value2');
delete from new_table where data='value2';
update new_table set data='value3' where data='value1';
This gives you the highest precision at no maintenance expense. It's guaranteed to hide records from 3 months ago and older, as of query time - that's 3 months exactly, down to fractions of a second. You can keep this perceived precision while following the 3 month restriction a bit less strictly, running the clean up task using cron, pg_cron or even triggers, less often.
If you can't have records older than exactly 3 months and you must hold records younger than exactly 3 months, you might want to set up a trigger/rule that will NOTIFY on a channel about incoming records and their scheduled deletion times, and a daemon that can LISTEN on that channel and sets up an at task (schtasks on Windows).
at now + 3 months -f delete_older_than_3_months_using_psql.sh
The script can be a regular delete that's just triggered at the time coinciding with the moment some records become outdated, but still has to search them all and find those, or you can save their primary keys in the cleanup command or let it pop() it off a FIFO queue somewhere.
How long is "3 months"
If you mean 90 days, you need to accept that the deletion dates will shift around based on month lengths within that 90-day difference from creation date.
with
test_dates(example) as
( values
('2023.01.01'::date),
('2023.02.01'::date),
('2024.02.01'::date),--leap year
('2023.03.01'::date))
select example, (example + '90 days'::interval)::date as "date 90 days later"
from test_dates;
-- example | date 90 days later
--------------+--------------------
-- 2023-01-01 | 2023-04-01
-- 2023-02-01 | 2023-05-02
-- 2024-02-01 | 2024-05-01 --leap year
-- 2023-03-01 | 2023-05-30
If you mean 3 months, you need to accept that it'll sometimes be longer or shorter:
with
test_dates(example) as
( values
('2023.01.01'::timestamp),
('2023.02.01'::timestamp),
('2024.02.01'::timestamp),--leap year
('2023.03.01'::timestamp),
('2023.04.01'::timestamp),
('2023.05.01'::timestamp),
('2023.06.01'::timestamp) )
select example, example + '3 months'::interval - example as "3 months length in days"
from test_dates;
-- example | 3 months length in days
-----------------------+-------------------------
-- 2023-01-01 00:00:00 | 90 days
-- 2023-02-01 00:00:00 | 89 days
-- 2024-02-01 00:00:00 | 90 days --leap year
-- 2023-03-01 00:00:00 | 92 days
-- 2023-04-01 00:00:00 | 91 days
-- 2023-05-01 00:00:00 | 92 days
-- 2023-06-01 00:00:00 | 92 days
And that it's interpreted differently by different systems and people: 'N months' interval literal in PostgreSQL:
select '01-31-2023'::timestamp + '1 month';--2023-02-28 00:00:00
select '02-28-2023'::timestamp + '1 month';--2023-03-28 00:00:00
select '02-29-2024'::timestamp + '1 month';--2024-03-29 00:00:00 --leap year
select '03-31-2023'::timestamp + '1 month';--2023-04-30 00:00:00
In bash, at understands it differently:
$ echo "command" | at "00:00 013123" + 1 month
job 14 at Fri Mar 3 00:00:00 2023
$ echo "command" | at "00:00 022823" + 1 month
job 15 at Tue Mar 28 00:00:00 2023
$ echo "command" | at "00:00 022924" + 1 month #leap year
job 16 at Tue Mar 29 00:00:00 2024
$ echo "command" | at "00:00 033123" + 1 month
job 17 at Mon May 1 00:00:00 2023
You should set cron jobs on your server this is best approach.
I have the following lines in a file. The first columns look like this (these are dates and time):
May 29 23:14:39
Dec 20 19:45:15
Nov 3 13:15:19
Sep 8 10:34:15
Mar 9 18:39:20
Jan 17 19:34:59
I would like to use awk to sort it by today's date. For example today is November 03 (Nov 3). Tomorrow it will be November 04 (Nov 4). The dates will be there accordingly as the days change and the file changes along. Now I would like the first line to be the today's date all the time / always. Is that even possible to sort it out like this using either awk, sed and the like?
Alphabetical sort does not work because it does 1,2,3,4 and so on and if today is January 20 for example, my first line will be January 1 anyway (not January 20) because it will do alphabetical order / sort.
Would appreciate any help / suggestions / pointers. Many thanks in advance.
P.S. Let me edit this as asked by Cyrus.
Well, the following code actually works, but... sed '/reject/!d' file.txt | sort -r -k2'... It goes like this: November 1, November, 2, November 3, and then October 31. I guess it grabs numbers like so 1,2,3,31. If I could get it to solve this it would also work. Thanks.
My desired output is to sort it by date with the current today's date to be the first line in my file all the time. However, the code above would also work for me if I could get it to count November 1, November 2, November 3, November 4 (instead of November 1, November 2, November 3, October 31).
P.S.S. That's another edit as per Ed's Morton request.
The dates are all there. I do not need to add anything. The only requirement is for today's date line to be the first one in that file all the time and then sort it out backwards in descending order. For example today is November 05, 2022 and this is the first line in the file like this Nov 5 12:45:89. Then all the other lines are for November 4, 3, 2, 1. Then all the other lines are for October 31, 30, 29, 28 and so on. It is supposed to go backwards and it has to start from the current date, that is from "today" all the time. For example tomorrow the first line has to be Nov 6 and everything else backwards. Then the day after tomorrow the first line has to be Nov 7 and everything else backwards and so on and so forth. I do not need to truncate anything. I do not need to add anything. I do not need to delete anything. All the data has to stay there and go backwards starting from the "current" day, whether it is today, tomorrow, after tomorrow and so on. I hope it's clear enough. Thanks.
It sounds like this, using any POSIX compliant versions of the tools, might be what you want but without expected output in the question it's a guess:
$ cat tst.sh
#!/usr/bin/env bash
awk -v today="$(date +'%F')" '
BEGIN {
OFS = "\t"
split(today,d,/-/)
year = d[1]
}
{
mthNr = index(" JanFebMarAprMayJunJulAugSepOctNovDec",$1) / 3
date = sprintf("%04d-%02d-%02d", year, mthNr, $2)
}
date <= today {
print date, $3, $0
}
' "${#:--}" |
sort -rk1,2 |
cut -f3-
$ ./tst.sh file
Nov 3 13:15:19
Sep 8 10:34:15
May 29 23:14:39
Mar 9 18:39:20
Jan 17 19:34:59
The above uses a DSU approach to solve the problem. It's sorting on both date and time so for the same date the output is similarly ordered by the time that day.
The output of date command has two spaces when the day number is less than 10. For example
Mon May 31 10:24:01 +0430 2020
Mon Jun 1 10:24:01 +0430 2020
For the first one, date | cut -d " " -f 3 returns 31. But, for the second one, it returns SPACE. Any way to fix that?
You could simple try awk to get the 3rd field, which will be definitely better than cut command.
date | awk '{print $3}'
Now why cut command is not working IMHO because space between May and 31 is equal but space between Jun and 1 is not equal so you need to use f4 in your cut command(for single digit date numbers with your current approach). Its better to use awk in this case.
Or In ideal scenario if you want to get only date then use date '+%d' to get date always in 2 digit as per #anubhava sir's comments.
I need my server to reboot after every 5 days of uptime
if it is not rebooted and again reboot after exactly 5 days and continue this process consecutively.
I would like to know how to do this with cronjob or any other script
You can use the dow, or Day of Week, part of the crontab syntax, to execute the command on every occurrence of a particular day, using numbers in the form:
0-6 Sunday to Saturday
1-7 Monday to Sunday
Or even shortened word notation such as WED or THU for Wednesday or Thursday
# m h dom mon dow command
00 07 * * 5 your-command-here
The example above would run the command at 7:00 am every Friday
I am building a report in SSRS.
In design mode it looks like this:
tat2 are values 1 through 192 and appear on the report like this:
1
2
3
...
192
I would like to know if there's a way to instead do something like this:
DAY 1 12:00AM
DAY 1 1:00AM
DAY 1 2:00AM
...
DAY 7 9:00PM
...
DAY 8 12:00AM
In other words, I would like to turn the numbers 1 through 192 into hours and days.
You could use Date.AddHours() for this - just create a new Date that's the start of any year and use
Date.AddHours(Fields!YourNumericField.Value)
This way you get rolling hours - will you ever have more than 192? What's the maximum range, as this would roll-over at 365. You could just mix and match and do an expression though like:
=Math.Ceiling(Fields!YourNumericField.Value / 24) & SomeDate.AddHours(Fields!YourNumericField.Value)
Something like that
I don't have SSRS on this machine to test though :P
Edit:
Ok so to get the base date you can use new DateTime(year, month, day)
http://msdn.microsoft.com/en-us/library/system.datetime.aspx
So the expression
="DAY " & Math.Ceiling(Fields!tat2.Value / 24) & " " & format(new DateTime(2000, 1, 1).AddHours(Fields!tat2.Value), "hh:mm tt")
This should give:
DAY 1 10:45 AM
Should work - if you want to change the format of the 10:00AM bit check this reference:
http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
"HH:mm" gives you 24 hour time + minutes e.g. 23:54
"hh:mm tt" is 12 hour e.g. 12:00 PM
Have a play
This can be easily done in the underlying query - not sure about doing it in SSRS:
SELECT
Tat2 / 24 + 1 as Day,
CAST(Tat2 % 24 AS CHAR(2)) + ':00 ' +
CASE WHEN Tat2 % 24 > 12 then 'PM' else 'AM' end as AMPM
FROM YourTable
This won't, of course, handle more than 365 days, because it doesn't months or years.