From: www.itworld.com
January 29, 2007 —
Send in your Unix questions today! |
See additional Unix tips and tricks
We looked at various ways to write scripts that would only run on the last day of the month in the column "Is this the last day of the month?". Determining whether an arbitrary day is the last day of the month can be done in a number of ways, using the cal and/or the date command. For example, if tomorrow is the first, then today must be the last day of the month. This gets us around the complexity of months with 28, 30 or 31 days.
When a reader recently asked how to write a script that would only run on the last workday of the month, a new twist was added to the old problem. A day is the last workday of a month only if it's a weekday and the following weekday falls in the next calender month. So, I came up with two approaches to solving the new challenge.
The most obvious solution is to use the human-friendly output of the cal command. Here's the output from the "cal" command for this month:
January 2007
S M Tu W Th F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
|
We can clearly see that Wednesday, the 31st is the last workday in January 2007. To calculate this in a script, we can use a clunky, but very straightforward command such as this:
cal | grep -v "^$" | tail -1 |
This would give us the last non-empty line in the cal output. We still need to pick out the last workday. If the last week in the month looked like this (as it will this June), we don't want to select the last date on the list as this would be a Saturday:
24 25 26 27 28 29 30 |
Instead, we would want to select the 29th -- the next-to-last date on the line. We can do this by counting the dates in the output and selecting the last date on the line only if there are fewer than seven dates.
#!/bin/bash
caldata=`cal | grep -v "^$" | tail -1`
numdays=`echo $caldata | awk '{print NF}'`
if [ $numdays -le 6 ]; then
echo $caldata | awk '{print $NF}'
else
echo $caldata | awk '{print $6}'
fi
|
A somewhat more elegant, though still highly readable, approach is to use the date command with time zone arguments. In this approach, we can determine that a day is the last workday of the month if it's a Monday, Tuesday, Wednesday or Thursday and the following day is the first or if it's a Friday and the following Sunday is either the first or the second.
#!/bin/bash
timezone=`date +%Z` # time zone
DoW=`date +%w` # day of week
tomorrow=`TZ=$timezone-24 date +%d` # today's date (e.g., 27)
dayafter=`TZ=$timezone-48 date +%d` # day after tomorrow
case $DoW in
[1-4]) if [ $tomorrow == 1 ]; then
lastworkday=true
fi;;
[5]) if [ $dayafter -le 2 ]; then
lastworkday=true
fi;;
esac
if [ $lastworkday ]; then
echo your code here
fi
|
The "TZ=$timezone-24 date +%d" and "TZ=$timezone-48 date +%d" commands look a little odd, but they represent commands such as these:
TZ=EST date +%d TZ=EST-24 date +%d |
Try this using your own time zone and you should get the calendar dates for today and tomorrow. Tomorrow's date is calculated by offsetting today's date by 24 hours and the day after by 48.
There are probably some excessively clever one-liners out there that can also be used to pick out the last workday of the month, though I suspect they're as dense as they are brilliant. If you have crafted one of these wonders, please share it.
ITworld.com