Good morning all,
I am in need to run different outputs based on next month first Wednesday or second Tuesday or each quarter otherwise run xxx
In short
if (Today +3 = First Saturday) or ( Today +2 = First Saturday) or (Today +1 = First Saturday) then run xxxx followed by
if (Today +3 = Second Tuesday of quarter) or ( Today +2 = Second Tuesday of quarter) or (Today +1 = Second Tuesday of quarter) then run yyyyy
Else run zzzzzz
Is this based around cron, like your previous question ?
I don't understand what you are asking for: "based on next month first Wednesday". You can't run something "next Wednesday" -- it runs right now, when you do the test.
For example, 1st May 2024 is Wednesday, so the first Saturday in May is the 4th. Which Wednesday do you want to run the script on -- 1st May or 24th April ?
Same for the second Tuesday of a quarter.
You also say both "otherwise run xxx" and "else run zzzzzz". Both cannot be correct.
my interpretation is the requester wants/needs to do date math ... but wants 'us' to do/show them ... as you remark @Paul_Pedant - looks much like their previous ... and i don't see any efforts on the requestor's part (i did some basics for the first part but stopped as no effort from the requester == no efforts from me)
Quite so. I also note the or conditions each imply the script runs for three consecutive "Todays". I suspect if the requirement was stated coherently (or with correct examples), the solution would be obvious.
Nevertheless, this kind of requirement is common enough: I would have expected by now that crontab would have allowed negative day_of_month, so -1 for last day, 2,-2 for second day and second-to-last day etc. But then I would want last Friday of month etc too.
I would create a calendar text file containing two fields, date and job to run.
Then create a cron job that calculates today's date and then greps the calendar to determine what to run. The calendar only has to contain the days that a job is to be run.
I am never keen on that technique. For me, it contains two issues.
(1) It is impossible to verify your calendar file, except visually. If you have a typo anywhere, it will simply run on the wrong day, or not at all. If it is programmed (and properly tested), it will work forever.
Testing the timing of cron jobs can be tricky. I usually set up to run every five or ten minutes, and to merely append to a log the date/time, and what task and args it would have run live. Then I can edit the script between runs to change the day numbers etc to exercise the logic fully. This avoids the issue of getting one test shot per month on your cron job.
(2) If you make a calendar file, it will only cover a certain period. It cannot (for example) cover multiple years, because the day-of-week and leap years rotate in predictable but unpleasant ways. The longer the period you cover, the more likely it is that the need to refresh the calendar will be overlooked.
As a lifetime contractor, I can never rely on any organisation continuing to do this kind of maintenance stuff. I can always rely on them emailing me fifteen months after I end my time, saying my system has not produced the expected reports for the last three months, and ordering me to show up the next day and fix the system for free, including producing the missing reports when they no longer have the inputs to do so.
Just realizing, Saturday and Tuesday are more than 2 days apart from each other, so the respective conditions are exclusive, and the main part can be optimized:
Presumably you intend to initiate this script from a crontab, though. You just need to have cron manage the simple stuff (like the minute, and hour), and have the script do nothing when none of the conditions are met. It is very cheap to have cron call a script that does nothing on most days, and more reliable than pushing the boundaries of crontab.
You can often optimise on the days too. For example, if you want to run on the last Friday of the month you can restrict the days in crontab. The last Friday in February (28 days) can only fall on the 22nd to the 28th. So you can tell cron the day range 22-31 (to accommodate the seven long months as well). The subsequent script check needs to deal with the case where there are two Fridays in that ten-day range, but it does not have to be called for the first 21 days of any month.
I understand the code but I can't comprehend the logic.
In this month (March 2024) the first Saturday is the 2nd.
So on Wed 28th Feb your dow3 check will run the script. On Thu 29th Feb, dow2 will pull the trigger. On Fri 1st March, dow1 will have its say. The quarterly version will do the same. The xxxx and yyyy scripts get run on three consecutive days which can span over two different months, or all be in the month preceding Sat 1st, or all be in the same month as the first Saturday if it is the 4th or later.
That seems somewhat illogical: I can't envisage how that business case could ever arise.
I don't think the original post has enough clarity to be sure this is actually the requirement. I don't understand the initial sentence, and I suspect the "in short" version is logically flawed.
This kind of thing can be exhaustively tested. All you need is a local version of date.x which fakes its output from a test calendar with a bunch of cases.
OK, so figuring out the quarter script execution was easier then I though by simply using crontab
Sharing here so others can benefit by my finding. Chose to use contab to lower the CPU over head as the original idea of having a bash script do all the work would have to be invoked every 5 min to make it useful (see below hourly run for more details).
What I haven;t figured out yet is how and many have shared in lots of post that finding the end of the month is problematic and I have yet to master a) find end of month then check what day of the week it is and set script to run on Wednesday to first Saturday.....will figure it out.
#.. Finding the date of the last day of the current month.
$ #.. Get the current year and month.
$ read -r dY dM <<<"$( printf '%(%Y %m)T' -1 )"
$ declare -p dY dM
declare -- dY="2024"
declare -- dM="03"
$ #.. Jump to the middle of next month (to avoid day steps).
$ read -r dY dM <<<"$( date -d "$dY-$dM-10 + 1 month" '+%Y %m' )"
$ declare -p dY dM
declare -- dY="2024"
declare -- dM="04"
$ #.. Step back 1 day from the first of that month.
$ read -r EndMth <<<"$( date -d "$dY-$dM-01 - 1 day" '+%F' )"
$ declare -p EndMth
declare -- EndMth="2024-03-31"
$ #.. Check it works over a year end.
$ read -r dY dM <<<"$( echo 2023 12 )"
$ read -r dY dM <<<"$( date -d "$dY-$dM-10 + 1 month" '+%Y %m' )"
$ read -r EndMth <<<"$( date -d "$dY-$dM-01 - 1 day" '+%F' )"
$ declare -p EndMth
declare -- EndMth="2023-12-31"
$ #.. Check it works on a leap year.
$ read -r dY dM <<<"$( echo 2024 02 )"
$ read -r dY dM <<<"$( date -d "$dY-$dM-10 + 1 month" '+%Y %m' )"
$ read -r EndMth <<<"$( date -d "$dY-$dM-01 - 1 day" '+%F' )"
$ declare -p EndMth
declare -- EndMth="2024-02-29"
$
Note on "Days steps": If you tell date "2023-01-30 + 1 month", it will get to 2023-02-30, which does not exist. It folds over to 2023-03-02, and you miss February entirely. So backing one day into the previous month gets you 28-Feb, not 31-Jan as required.
If using ksh93, then you can use strong builtin date properties.
You can look more examples, fpmurphy blog. (epoch, epoch with nanosecs and so on ...).
# today 2024-04-03
# default is today or use attribute "now" which return current time
# 1st day of month
printf "1st day last month %(%Y-%m-%d)T\n" "last month"
# 2024-03-01
# is same as
printf "1st day last month %(%Y-%m-%d)T\n" "now,last month"
# 2024-03-01
printf "1st day -10 month %(%Y-%m-%d)T\n" "last 10 month"
# 2023-06-01
printf "1st day this month %(%Y-%m-%d)T\n" "month"
# 2024-04-01
printf "1st day next month %(%Y-%m-%d)T\n" "next month"
# 2024-05-01
# last day of month
printf "last day last month %(%Y-%m-%d)T\n" "final last month"
# 2024-03-31
printf "last day this month %(%Y-%m-%d)T\n" "final month"
# 2024-04-30
printf "last day next month %(%Y-%m-%d)T\n" "final next month"
# 2024-05-31
printf "last day -10 month %(%Y-%m-%d)T\n" "final last 10 month"
# 2023-06-30
printf "last day +10 month %(%Y-%m-%d)T\n" "final next 10 month"
# 2025-02-28
# same using some day, ex. 2024-02-24
day="2024-02-24"
# 1st day of month
printf "1st day last month $day = %(%Y-%m-%d)T\n" "$day,last month"
# 2024-01-01
# last day
printf "last day, this month $day = %(%Y-%m-%d)T\n" "$day,final month"
# month 2024-02-29
# next N month last day
printf "last day, next 1 month $day = %(%Y-%m-%d)T\n" "$day,final next 1 month"
# 2024-03-31
# more ...
printf "3rd wednesday july 2022 %(%Y-%m-%d)T\n" "3rd wednesday july 2022"
# 2022-07-20
printf "next wednesday %(%F)T\n" "next wednesday"
# 2024-04-10