2

I need your help to contruct a cron job to run the second last working day of every month. I can do LW as shown below but i am not sure how to do second last. I need your help and advise here please.

@MWE has kindly provided me a possible solution but it does not seem to work in all test scenarios.

code:
#!/bin/bash
date
WEEKDAY=$(date +%u)
# If detect the montday in 2 days.
if [ $(date +%d -d "2 day") -eq 1 ] && [ ${WEEKDAY} -lt 5 ] ; then
   echo "This is the 2. last working day of the month"
# If detect the montday in 5 days.
elif [ $(date +%d -d "5 day") -eq 1 ] && [ ${WEEKDAY} -eq 5 ] ; then
   echo "This is the 2. last working day of the month"
else
  exit
fi

Test scenario: Ive amended the system date on the server to be 29th August 2019 (**this is the second last working date on the system)

the script in debug mode exits out:

[oracle@rdbauroral01v ~]$ bash -x ./crontest.sh
+ date
Thu Aug 29 13:28:20 UTC 2019
++ date +%u
+ WEEKDAY=4
++ date +%d -d '2 day'
+ '[' 31 -eq 1 ']'
++ date +%d -d '5 day'
+ '[' 03 -eq 1 ']'
+ exit

appreciate your help here..

nick
  • 31
  • 1
  • 6
  • 1
    Your best bet with this sort of unusual cron expression is to run daily and let the script itself handle the more complicated logic. – ceejayoz Jul 18 '19 at 15:08

4 Answers4

1

Just as simple idea to solve it in the script not in crontab.:

Last day of a month is always this day, when tomorrow = 1. of a month.

That means: Let get out the current date from epoch in seconds, and add 24h hours. Then lets get out what day of month it is:

TIMESTAMP=$(date +%s)
TIMESTAMP_TOMORROW=$(( $TIMESTAMP + 86400 ))

Figure out the month day of tomorrow:

DAYOFMONTH=$(date --date="@${TIMESTAMP_TOMORROW}" +%e)

if DAYOFMONTH == 1 today is the last day of month. Do, what you want, then. if not == 1, exit.

Alternatives can be found here: https://stackoverflow.com/questions/6139189/cron-job-to-run-on-the-last-day-of-the-month

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

or:

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh

Last but not Least. Multiply $(( $TIMESTAMP + 86400 * 2 )) , for the 2. last day.

or:

TOMORROW=$(date +%d -d "2 day")

or:

[ $(/bin/date -d +2day +\%d) -eq 1 ]
  • Apologies for my ignorance but the above would give be "second to last day of the month" but what i seek is "second to last weekday of the month" – nick Jul 18 '19 at 14:40
  • Well, Second last weekday .... is the 5. day of a week, right? So try to understand the way i go. In your case, find out, what day of wekk it is. Than verify, that tomorrow is between. 1 max. 6 of month. Depends on your setting, if weekday starts by sunday or monday. –  Jul 18 '19 at 14:42
  • Hi, apologies in advance, As i very new to unix, how do i test the next execution of dates of the above to know that it would do exactly how you have described? especially i am keen to test this logic as stated by you --- 0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh – nick Jul 18 '19 at 14:47
0

To get out the 2. last working day of month. %u start with day 1 at monday. %w with sunday.

  1. last Workingday is typically thursday. So the 4. weekday. We have to look now 6 days in to future. -> Means, last 2. Workingday of month must be before the 5.th day of month.
WEEKDAY=$(date +%u)
TOMORROW=$(date +%d -d "5 day")
if [ ${WEEKDAY} -eq 4 ] && [ $TOMORROW -lt 5 ]; then
echo "This is the 2. last working day of the month"
fi
0

In crontab you can limit execution to Weekdays and Monthdays. Both in combination are a good start. So you can ensure the script only runs on a workingday. On Monthday aware February (28 days etc.) or just 30 days. it doesn't end at 31 for every month! So maybe use day 20-31, to be safe.

     field          allowed values
          -----          --------------
          minute         0-59
          hour           0-23
          day of month   1-31
          month          1-12 (or names, see below)
          day of week    0-7 (0 or 7 is Sunday, or use names)

Crontabentry like:

0 0 20-31 * 1-5 ./scriptname

The part is simple to find out, wha ist the second 3rd or 4. Weekday. Why? The script runs by crontab only on Workingdays. So we needen't care about saturday or sunday. Alternate way, we could check if the Weekday $(date +%u) in 1-5. This are working days.

The code is now simple:

If in 2 days, it's the first day of month... its the second last working day. But, what about Saturday and Sunday?

You need an case now. From Monday to Thursday will allways follow a working day. So the rules, if it is in 2 days the 1. Monthday, it is the 2. Last Workingday. Only on Friday there must be the first of month in 5 days to be the second Workingday. Or the first of a month must be an Thuesday.

WEEKDAY=$(date +%e)
# 1. If detect the montday in 2 days.
if [ $(date +%d -d "2 day") -eq 1 ] && [ ${WEEKDAY} -lt 5 ] ; then
   echo "This is the 2. last working day of the month"
# 1. If detect the montday in 5 days.
elif [ $(date +%d -d "5 day") -eq 1 ] && [ ${WEEKDAY} -eq 5 ]
   echo "This is the 2. last working day of the month"
else 
  exit
fi

Hopefully i don't miss anything.

  • thanks. Someone suggested create cron 26 -31st check if if(tomorrow == last weekday) but im not sure how that is going to work? – nick Jul 18 '19 at 16:38
  • Will not work. 2. Last Weekday isn't last Workingday of month. See Example of January. Maybe the 30.01 is monday. Last Weekday is Friday so tomorrow will not be the last weekday but monday 30.01 second last Workday of month. –  Jul 18 '19 at 22:21
  • Done and reedited. –  Jul 18 '19 at 23:05
  • Hello, the above throws error ./cron_test2.sh: line 8: syntax error near unexpected token `else' ./cron_test2.sh: line 8: `else ' Also under which condition my job should run as both "if" statements echos "2nd last working day. – nick Jul 19 '19 at 09:25
  • okay the script syntax is okay. Do i still need to set the cron job as "0 0 20-31 * 1-5 ./scriptname" ? – nick Jul 19 '19 at 09:58
0

Do i still need to set the cron job as "0 0 20-31 * 1-5 ./scriptname"

No! But, does it make sense to execute it on any day?

MWE
  • 19
  • 5
  • 1
    Welcome to Server Fault! This does not answer the question. If you have another question, please ask it by clicking the [Ask Question](http://serverfault.com/questions/ask) button. – Gerald Schneider Jul 19 '19 at 13:12
  • Gerarld Schneider, it answers his question in a comment. But i'm not able to answer his comment directly. I'm realy sorry about this... – MWE Jul 19 '19 at 13:21
  • 2
    Unfortunately the StackExchange sites don't quite work this way - since your answer isn't an answer to the actual question, it's hard to connect it to the comment you're thinking of. The only advice I can give is to spend a little time here to answer questions, and you'll soon get enough reputation points to be able to comment properly. – Jenny D Jul 19 '19 at 13:34
  • Thisfore i added the "Quote" of the question in the comment. – MWE Jul 19 '19 at 13:39
  • Can someone please tell me the final version so i can test and implement and what should be the crontab setup? – nick Jul 19 '19 at 13:50
  • https://serverfault.com/a/975796/532351 At End and fixed up. In the else case there is an exit. So script will automatically end if it's not the 2. Last Workingday. – MWE Jul 19 '19 at 13:55
  • is there any way round to test this script before 30th of this month (2nd last working day of the month) to know its working? – nick Jul 19 '19 at 14:30
  • yes, Set the Date / sysdate on the System you will Test the Script to a Date matching to second last workday of month. – MWE Jul 19 '19 at 15:03
  • that is going to hard as im on a unix server where i dont have rights to change the sysdate. Otherwise i will set the job as "0 0 20-31 * 1-5 ./scriptname" and monitor on the day . Also in the script i will replace the "echo statement" in both places and add my code twice, right? – nick Jul 19 '19 at 17:13
  • Come on nick. It isn't that big thing to set up a test system on virtualisation or install cygwin on maybe your windows server. – MWE Jul 19 '19 at 18:02
  • Hello MWE, hope all is well. Not sure i posted it in the right thread so re -posting . Followed your advice and it does not work. "[oracle@rdbauroral01v ~]$ date Tue Jul 30 12:07:48 UTC 2019" – nick Jul 22 '19 at 11:14
  • ++ date +%e + WEEKDAY=30 ++ date +%d -d '2 day' + '[' 01 -eq 1 ']' + '[' 30 -lt 5 ']' ++ date +%d -d '5 day' + '[' 04 -eq 1 ']' + exit – nick Jul 22 '19 at 11:15
  • the above was in debug mode showing the scenes behind. Please advise. – nick Jul 22 '19 at 11:16
  • Depending on distribution and version of date use: -d '2day' instead of -d ' 2 day' – MWE Jul 22 '19 at 11:46
  • done but in vain. – nick Jul 22 '19 at 11:53
  • ++ date +%e + WEEKDAY=30 ++ date +%d -d 2day + '[' 01 -eq 1 ']' + '[' 30 -lt 5 ']' ++ date +%d -d 5day + '[' 04 -eq 1 ']' + exit – nick Jul 22 '19 at 11:54
  • What do you think should it do? Is it today the 2. laste working day? It des a exit, as expected. – MWE Jul 22 '19 at 12:06
  • i think may be you missed my initial comment. I have changed the sysdate to be 30th "[oracle@rdbauroral01v ~]$ date Tue Jul 30 12:07:48 UTC 2019" so yes today is technically the last working day on the server/ – nick Jul 22 '19 at 12:07
  • To be sure, the sysdate is correct, add date in a new row at start of the script. _Just toge tout which date ist realy set. – MWE Jul 22 '19 at 12:09
  • + date Tue Jul 30 13:05:00 UTC 2019 ++ date +%e + WEEKDAY=30 ++ date +%d -d 2day + '[' 01 -eq 1 ']' + '[' 30 -lt 5 ']' ++ date +%d -d 5day + '[' 04 -eq 1 ']' + exit – nick Jul 22 '19 at 12:11
  • It does show the correct date but surprisingly the script just exits – nick Jul 22 '19 at 12:13
  • @nick: Add this question from a comment as an edit to your original question, so this post is actually an answer to part of your question. Otherwise, this will have to be deleted. – Sven Jul 22 '19 at 12:16
  • WEEKDAY=$(date +%u) thats wrong of it. -> %u not %e. – MWE Jul 22 '19 at 12:17
  • Hello Sven, Just so i got this correct, MWE is assisting me resolve this issue. Would you like me to post the errors to the initial solution provided by MWE to my original post - https://serverfault.com/q/975766/532205 ? thanks – nick Jul 22 '19 at 12:21
  • @MWE now that July shows the echo but now set to 2 last working day for Aug and it again exits. + date Thu Aug 29 13:29:31 UTC 2019 ++ date +%u + WEEKDAY=4 ++ date +%d -d 2day + '[' 31 -eq 1 ']' ++ date +%d -d 5day + '[' 03 -eq 1 ']' + exit – nick Jul 22 '19 at 12:31
  • 1
    @nick: Yes, please edit your question to contain the part MW is referring to. Answers on [SF] **must** answer the question, not discuss something else, like comments. – Sven Jul 22 '19 at 12:42
  • Have done so Sven. Apologies for my incorrectness. – nick Jul 22 '19 at 12:52
  • @MWE Hi, you should add your tips inside your answer too, as comment can be deleted. Don't forget to update your answer with the suggested information(s). After that lets clean up/delete unecessary comment(s) – yagmoth555 Jul 22 '19 at 16:15