Jompa
Jompa

Reputation: 125

Find the last Saturday of the month in a bash script

I'm making a backup script in which I need to find the last Saturday of each month.

I've tried different approaches to finding the day itself, which works splendidly themselves.

The problem is, when I try putting them into my script I always get the error code ./test.sh: line 13: [: 29: integer expression expected.

This is my code:

#!/bin/bash

LASTSAT=$(ncal | grep Sa | awk '{print$(NF-0)}')
SATURDAY="6"

DAY=$(date +"%u")
DATE=$(date +"%d")

echo "$DAY"
echo "$DATE"
echo "$LASTSAT"

if [ $DATE -eq $LASTSAT  ]
then
 echo "sista lördagen..."
fi

I got the tip to change the if statements to [ "$DATE" = "$LASTSAT" ] which erased the error itself, but the script will somehow not equal 27 to 27 (to take this month as an example).

I also tried another approach to finding the last Saturday which was LASTSAT=$(cal|awk '{if(NF==7){SAT=$7}};END{print SAT}'), but it returns the exact same error if I use -eq and doesn't equal 27 to 27 using " with =

I'm very confused and out of ideas and I have searched the internet and copied the exact lines others been using but it all ends up the same.

What am I doing wrong?

Upvotes: 3

Views: 11374

Answers (8)

Here is solution

if  [[ $(date +"%a") = "Sat" ]] && [[$(date -d "+7 days" +%m) != $(date +%m)]]
then
  echo "Last saturday of month"
fi

Upvotes: 0

user10145222
user10145222

Reputation: 1

How about this one:

if [ `date +%u` = 7 ] && [ `date +%m` != `date -d +week +%m` ]; then
  echo "sista lördagen..."
fi

... or simply via crontab:

0 0 * * 0 [ `date +\%m` != `date -d +week +\%m` ] && /path/to/backup.sh

Upvotes: -1

Marian Kamenistak
Marian Kamenistak

Reputation: 1

#!/bin/bash
set -e

if [ "$#" -ne 2 ]; then
    echo "Usage: weekDayOfMonth.sh [weekOfMonth 1..4] [DayOfWeek where Sun=1]"
    exit 2;
fi

weekOfMonth=$1
dayOfWeek=$2 #starts with Sun(=1)

echo "weekOfMonth: $weekOfMonth";
echo "dayOfWeek: $dayOfWeek";

# cal starts with Sun, 3rd column is for Tue, 2nd row is 2nd Tue of month:
# Sun=1, dayIdx=0
# Mon=2, dayIdx=3
# Tue=3, dayIdx=6
# Wed=4, dayIdx=9
dayIdx=$(((dayOfWeek-1)*3))
expectedDayOfMonth=$(cal -s | sed "s/^.\{$dayIdx\}\(.\{3\}\).*$/\1/" | sed 's/^[ ]//g' | grep -o '[0-9]*' | head -$weekOfMonth | tail -1)
todaysDay=$(date +%d | sed 's/^0*//')

echo "expectedDayOfMonth: $expectedDayOfMonth";
echo "todaysDay: $todaysDay";

if [ ${expectedDayOfMonth} -ne ${todaysDay} ]; then
       echo "Today is NOT requested day of month, exiting with error code.";    
       exit 1;
fi

Upvotes: 0

ghoti
ghoti

Reputation: 46846

You can easily get the last Saturday of the month using the date command, which spits out data in strftime() format. But date's syntax will depend on your operating system.

In FreeBSD or OSX, there's a -v option to "adjust" the date that gives you lots of control:

[ghoti@pc ~]$ date -v+1m -v1d -v6w '+%a %d %b %Y'
Sat 03 Nov 2012
[ghoti@pc ~]$ date -v+1m -v1d -v6w -v-1w '+%a %d %b %Y'
Sat 27 Oct 2012

The idea here is that we'll move 1 month forward (+1m), back up to the first of the month (1d), then move to the 6th day of the week which is Saturday (6w). For demonstration purposes, the first line shows the first saturday of next month, and the second line shows the date one week (-v-1w) earlier.

Alternately, if you wanted to put some math in your bash script, you could do something like this:

#!/usr/local/bin/bash

# Get day-of-the-week for the first-of-the-month:    
firstofmonth=$(date -j -v+1m -v1d '+%u')
     #                   ^
     #                   +  This is the relative month to current.

# Subtract this from 7 to find the date of the month
firstsaturday=$((7 - $firstofmonth))

# 7 days before that will be the last Saturday of the previous month
lastsaturday=$(date -j -v+1m -v${firstsaturday}d -v-7d '+%Y-%m-%d')

With the -v option, 1 is January, 2 is February, etc. Or it can be relative, as I've shown here, with +1 for next month, -1 for last month, etc.


In Linux, date uses a -d option that interprets a text description of the date. So:

#!/bin/bash

firstofmonth=$(date -d '+1 months' '+%Y%m01')
firstsaturday=$(date -d "$firstofmonth" '+%Y-%m')-$(( 7 - $(date -d "$firstofmonth" '+%u') ))
lastsaturday=$(date -d "$firstsaturday -7 days" '+%d')

Note that if you're using cron, you can simplify this. You know that the last Saturday will sit within the last 7 days of the month, so we can start by using cron to limit things to Saturday, then check for the last of the month within the script. This will run the script every Saturday, but it will do nothing except when it's supposed to. The cron tab would be, say,

#  ↙ "0 0"=midnight
0 0 * * 0 /path/to/script.sh
#        ↖ 0=Sunday

And script.sh would start with:

#!/bin/bash

if [[ $(date '+%d' -lt $(date -d "$(date -d '+1 month' '+%Y%m01') -7 days" '+%d') ]]; then
  exit
fi

You could also put this test within the crontab, though it would look a little uglier because you'd need to escape the percent signs:

0 0 * * 0 \
  test $(date '+\%d') -ge $(date -d "$(date -d '+1 month' '+\%Y\%m01') -7 days" '+\%d') \
    && /path/to/command

Upvotes: 3

rici
rici

Reputation: 241701

How about this one:

last_saturday () 
{ 
    local Format;
    if [[ $1 =~ ^\+.* ]]; then
        Format=$1;
        shift;
    fi;
    local FirstOfNext="${1:-$(date +%B)} 1 $2 + 1 month";
    date $Format -d "$FirstOfNext - 1 day - 
                     $(($(date +%u -d "$FirstOfNext") % 7)) day"
}

eg:

$ last_saturday
Sat Oct 27 00:00:00 PET 2012
$ last_saturday November
Sat Nov 24 00:00:00 PET 2012
$ last_saturday June
Sat Jun 23 00:00:00 PET 2012
$ last_saturday June 2013
Sat Jun 29 00:00:00 PET 2013
$ last_saturday +%Y-%m-%d June 2013
2013-06-29

Upvotes: 1

Steve
Steve

Reputation: 54392

Here's one way to print the last Saturday in each month using date:

for i in {1..12}; do
    for j in {1..7}; do
        date=$(date -d "$i/1 + 1 month - $j day" +"%w %F")
        # day of week (1..7); 1 is Monday
        if [ ${date:0:1} -eq 6 ]; then
            echo ${date:2}
        fi
    done
done

Results:

2012-01-28
2012-02-25
2012-03-31
2012-04-28
2012-05-26
2012-06-30
2012-07-28
2012-08-25
2012-09-29
2012-10-27
2012-11-24
2012-12-29

If you'd just like to find the last Saturday in the current month, try:

for j in {1..7}; do
    month=$(date +"%m")
    date=$(date -d "$month/1 + 1 month - $j day" +"%w %F")
    # day of week (1..7); 1 is Monday
    if [ ${date:0:1} -eq 6 ]; then
        echo ${date:2}
    fi
done

Result:

2012-10-27

Please note, that you can change the output format of these results in the above scripts simply by changing %F to any of the formats date has to offer. See man date.

Upvotes: 2

James Waldby - jwpat7
James Waldby - jwpat7

Reputation: 8711

During testing I changed the target day and some of the variable names, but the following script works, for the next-to-last Sunday of the month. The critical change, relative to your script, was adding the -h switch to ncal, to turn off highlighting of the current day. The highlighting characters apparently come through when awk prints the field, but aren't visible when you do the echos. Note, you can drop the grep after ncal via an awk match.

#!/bin/bash

DoDay=$(ncal -h |awk '/Su/ {print $(NF-1)}')
Datum=$(date +%d)
echo $Datum Datum
echo $DoDay DoDay

if [[ $Datum == $DoDay ]]
then
    echo "sista lördagen..."
else
    echo "doh"
fi

Upvotes: 3

agirish
agirish

Reputation: 470

The script works fine on my System (Ubuntu - Bash $). I just modified the script to print "!sista lördagen..." if the day wasn't the last saturday and this was the output:
$sh abc.sh
7
21
27
!sista lordagen...

Upvotes: 0

Related Questions