Solved

Shell Script

Posted on 2014-04-01
17
637 Views
Last Modified: 2014-04-09
Hi

i have cronjob shell script  which we want to run all week days in a year except  first 3 business days of month and last two business days  of month     We dont want script not on this particular days.    This applies all months in a year.

Can we do some modificaton to this script or modify the cron enty

current crontab entry

04 00 * * 1-5 /sdfdf/sdfd.sh

Thanks
0
Comment
Question by:vadicherla
  • 11
  • 4
  • 2
17 Comments
 
LVL 19

Expert Comment

by:simon3270
ID: 39970178
I *had* seen your previous question in the "community" zone, so wasa bit surprised when it was closed!

Just have this at the start of your shell script.
#!/bin/bash

# exit if in first 3 business days of the month or last 2

sday=0  # number of business days we have counted
cday=0  # day number we are looking at now
tday=$(date '+%d')  # today's day of the month
#tday=$1  # temp for testing - day is parameter
maxstart=3
maxend=2
ymdate=$(date "+%Y/%m/")
#ymdate=$2  # temp for testing
mdate=$(date '+%e')
#mdate=$3  # for testing
ydate=$(date '+%Y')
#ydate=$4  # for testing

# First check whether today is in the first $maxstart days of the month
while [ $tday -gt $cday -a $sday -le $maxstart ]; do
  cday=$(expr $cday + 1)
  c0day=$(printf "%02d" $cday)
  if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxstart ]; then
  echo In first $maxstart business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

# Now check the end of the month
# Get the last day of the month
dom=(0 31 28 31 30 31 30 31 31 30 31 30 31)
if [ ! \( $(expr $ydate % 4) -ne 0 -o \( $(expr $ydate % 400) -ne 0 -a $(expr $ydate % 100) -eq 0 \) \) ]; then
  dom[2]=29
fi

cday=$(expr ${dom[${mdate}]} + 1)
sday=0
while [ $tday -lt $cday -a $sday -le $maxend ]; do
  cday=$(expr $cday - 1)
  c0day=$(printf "%02d" $cday)
  if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxend ]; then
  echo In last $maxend business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

Open in new window

0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970189
By the way, most of the code will work in most modern shells, but the array initialisation is a bit bash-specific (though it may work in other shells too).  If it complains about the "dom=(0 3" line, replace it with
    dom[0]=0
    dom[1]=31
    dom[2]=28
and so on up to
    dom[12]=31

The "expr" statements could all also be replaced by neater shell versions, but I didn't want to be too bash-specific if I could help it!
0
 
LVL 27

Expert Comment

by:serialband
ID: 39970205
Excluding Leap Years, would this work for you?

04 00 4-29 1,3,5,7,8,10,12 1-5 /sdfdf/sdfd.sh
04 00 4-28 4,6,9,11 1-5 /sdfdf/sdfd.sh
04 00 4-26 2 1-5 /sdfdf/sdfd.sh
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970248
I don't think you can do it sensibly with crontab entries alone.  The trouble is that a line like:

    04 00 4-26 2 1-5 /sdfdf/sdfd.sh

will run if day of month is 4 to 26 OR day of week is 1 to 5 (not AND).

So if the 1st is a Monday, it will run.

My code assumed that you stayed with the original "04 00 * * 1-5" entry, so the script was only called Monday to Friday (but it would be simple to add that check to the script if you preferred).
0
 

Author Comment

by:vadicherla
ID: 39970449
Hi Expert

Do  you think it will easaier to implement if we change the script to run all 7 days a week except first 3 business days of month and last 2 business day of month??

Thanks
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970464
In that case, you could modify serialband's solution to

04 00 4-29 1,3,5,7,8,10,12 * /sdfdf/sdfd.sh
04 00 4-28 4,6,9,11 * /sdfdf/sdfd.sh
04 00 4-26 2 * /sdfdf/sdfd.sh

with the limitation that it won't work exactly in leap years.
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970477
Then at the top of the script have:
if [ $(date '+%u') -ge 6 ]; then
    exit 0
fi

Open in new window

to avoid running the rest of the script on Saturday or Sunday.
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970485
Ah, take that back.  The crontab solution stops the script running on the first three days of the month - not the first three *business* days.  That's why my shell script does checking of the day of the week (the "if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]" bit).

Note that my script doesn't cope with business holidays (e.g. January 1 is not a business day in many countries), but that would get quite complex!
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 27

Expert Comment

by:serialband
ID: 39970611
I've never done both day of the week and day of month together, so I forgot that it was an 'or', not an 'and' for that combination.
 
Maybe you could incorporate the test in the cron to simplify your subsequent tests within the script for the first 3 and last 2 business days.

04 00 4-29 1,3,5,7,8,10,12 * test $(date +$u) -ge 6 &&/sdfdf/sdfd.sh
04 00 4-28 4,6,9,11 * test $(date +$u) -ge 6 && /sdfdf/sdfd.sh
04 00 4-26 2 * test $(date +$u) -ge 6 && /sdfdf/sdfd.sh
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970705
I'd be wary of making cron entries any more complex than they need to be (there's also quite a short line length limit in some of the crons I've used).  And just to complicate matters, you'd have to escape the % in the "date +%u" bit because cron uses % as an end-of-line marker.

It would be just as easy to add that check in the script.  If it does need to count business days rather than just days, you'll need my complicated code, and adding the "date +%u" check I gave a couple of replies ago would be a trivial addition.
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970712
So, vadicherla, does the "business" days count mean that if, for example, the 1st and 2nd of the month were Saturday and Sunday, then the first three business days would be the 3rd, 4th and 5th, so you wouldn't run the script until Thursday the 6th?

Also, would you rather not insert this extra code into your existing script (the "/sdfdf/sdfd.sh" in your example)?  In that case, you could have a script which just contains the date checking code, and if all of the tests pass, it could then run your original script - that would be very easy to set up (just let me know and I'll show you how).
0
 

Author Comment

by:vadicherla
ID: 39970754
So, vadicherla, does the "business" days count mean that if, for example, the 1st and 2nd of the month were Saturday and Sunday, then the first three business days would be the 3rd, 4th and 5th, so you wouldn't run the script until Thursday the 6th?

This is correct.  

Also, would you rather not insert this extra code into your existing script (the "/sdfdf/sdfd.sh" in your example)?  In that case, you could have a script which just contains the date checking code, and if all of the tests pass, it could then run your original script - that would be very easy to set up (just let me know and I'll show you how).

Yes this will be a  good solution
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39970813
OK, I've added some extra code to the end of the script.  I've also fixed up a couple of bugs (the old code would have failed in September, oddly!), and added the "only run on business days" check that I mentioned earlier.  Note that I have commented out the "echo" commands I had to say why it was executing.  This is because cron commonly emails the user if a cron job produces any output (on the assumption that successful UNIX/Linux code usually produces no output - output implies a problem).
#!/bin/bash

# Exit if in first 3 business days of the month or last 2, or not a business day.
# If parameters are provided, they are run if the above tests do not exit.

sday=0  # number of business days we have counted
cday=0  # day number we are looking at now
tday=$(date '+%d')  # today's day of the month
#tday=11  # temp for testing
maxstart=3
maxend=2
ymdate=$(date "+%Y/%m/")  # start of date as YYYY/MM/
#ymdate=2014/04/  # temp for testing
mdate=$(date '+%m')       # Month, Jan=01
#mdate=04  # for testing
mdate=$(echo $mdate | sed 's/^0//')  # strip leading 0
ydate=$(date '+%Y')       # Year, eg 2014
#ydate=2014  # for testing

# First check that this is a business day
if [ $(date '+%u') -ge 6 ]; then
    # echo This is not a business day
    exit 0
fi

# Now check whether today is in the first $maxstart days of the month
while [ $tday -gt $cday -a $sday -le $maxstart ]; do
  cday=$(expr $cday + 1)
  c0day=$(printf "%02d" $cday)
  if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxstart ]; then
  # echo In first $maxstart business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

# Now check the end of the month
# Get the last day of the month
dom=(0 31 28 31 30 31 30 31 31 30 31 30 31)
if [ ! \( $(expr $ydate % 4) -ne 0 -o \( $(expr $ydate % 400) -ne 0 -a $(expr $ydate % 100) -eq 0 \) \) ]; then
  dom[2]=29
fi

cday=$(expr ${dom[${mdate}]} + 1)
sday=0
while [ $tday -lt $cday -a $sday -le $maxend ]; do
  cday=$(expr $cday - 1)
  c0day=$(printf "%02d" $cday)
  if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxend ]; then
  # echo In last $maxend business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

#Now, if a parameter has been provided, run it, and any parameters passed to it
if [ $# -ge 1 ]; then
  cmd=$1
  shift
  exec $cmd "$@"
fi

Open in new window


save this script as, say, busdaysonly.sh, make it executable, then your crontab entry would be

    4 0 * * * /path/to/busdaysonly.sh /sdfdf/sdfd.sh
0
 

Author Comment

by:vadicherla
ID: 39976569
Hi

How we are going to handle the fedral holidays with this script.
0
 
LVL 19

Expert Comment

by:simon3270
ID: 39976873
As I said above, adding holiday checking is a whole lot more complicated! It really depends on how automated you need it to be.

The best bet, and similar to a method I've used in a spreadsheet, is to have a file with all of the federal holiday dates over as many years as you are going to use this program, with one date per line in YYYY/MM/DD format. Then in the test that does the "date +%u... -lt 6" bit, also test that the date you are checking is not in that file. I'm not at a computer at the moment, but I'll test it out as soon as I can.
0
 

Author Comment

by:vadicherla
ID: 39977096
thank you
0
 
LVL 19

Accepted Solution

by:
simon3270 earned 500 total points
ID: 39977591
I think I have it.

Create a file called holidays.txt, with the holiday dates, one per line, as YYYY/MM/DD  For example
2014/01/01
2014/04/18
2014/04/21
2014/12/25

Open in new window

(those are New Year's Day, Good Friday, Easter Monday and Christmas Day this year, just as examples).

Then use the following script.  It's the same as before except I set up a "holidayfile" variable (on line 20) - set that to the path to your holidays.txt file.

Then the lines which do "date +%u... -lt 6" also do a grep -q for the same date in the holiday file.  If the day-of-week is less that 6, and the date is NOT in the file, it's counted as a business day.

Let me know how it goes!
#!/bin/bash

# Exit if in first 3 business days of the month or last 2, or not a business day.
# If parameters are provided, they are run if the above tests do not exit.

sday=0  # number of business days we have counted
cday=0  # day number we are looking at now
tday=$(date '+%d')  # today's day of the month
#tday=11  # temp for testing
maxstart=3
maxend=2
ymdate=$(date "+%Y/%m/")  # start of date as YYYY/MM/
#ymdate=2014/04/  # temp for testing
mdate=$(date '+%m')       # Month, Jan=01
#mdate=04  # for testing
mdate=$(echo $mdate | sed 's/^0//')  # strip leading 0
ydate=$(date '+%Y')       # Year, eg 2014
#ydate=2014  # for testing

holidayfile=/path/to/holidays.txt

# First check that this is a business day
if [ $(date '+%u') -ge 6 ]; then
    # echo This is not a business day
    exit 0
fi

# Now check whether today is in the first $maxstart days of the month
while [ $tday -gt $cday -a $sday -le $maxstart ]; do
  cday=$(expr $cday + 1)
  c0day=$(printf "%02d" $cday)
  if test $(date '+%u' --date=${ymdate}${c0day}) -lt 6 && ! grep -q ${ymdate}${c0day} $holidayfile ; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxstart ]; then
  # echo In first $maxstart business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

# Now check the end of the month
# Get the last day of the month
dom=(0 31 28 31 30 31 30 31 31 30 31 30 31)
if [ ! \( $(expr $ydate % 4) -ne 0 -o \( $(expr $ydate % 400) -ne 0 -a $(expr $ydate % 100) -eq 0 \) \) ]; then
  dom[2]=29
fi

cday=$(expr ${dom[${mdate}]} + 1)
sday=0
while [ $tday -lt $cday -a $sday -le $maxend ]; do
  cday=$(expr $cday - 1)
  c0day=$(printf "%02d" $cday)
  if [ $(date '+%u' --date=${ymdate}${c0day}) -lt 6 ]; then
    sday=$(expr $sday + 1)
  fi
done

if [ $sday -le $maxend ]; then
  # echo In last $maxend business days - date $tday, bus days $sday, checked $cday days
  exit 0
fi

#Now, if a parameter has been provided, run it, and any parameters passed to it
if [ $# -ge 1 ]; then
  cmd=$1
  shift
  exec $cmd "$@"
fi

Open in new window

0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Setting up Secure Ubuntu server on VMware 1.      Insert the Ubuntu Server distribution CD or attach the ISO of the CD which is in the “Datastore”. Note that it is important to install the x64 edition on servers, not the X86 editions. 2.      Power on th…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
Connecting to an Amazon Linux EC2 Instance from Windows Using PuTTY.
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now