LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Bash script for printing days in a specific month (taking into account a leap year) (http://www.linuxquestions.org/questions/programming-9/bash-script-for-printing-days-in-a-specific-month-taking-into-account-a-leap-year-941666/)

shayno90 04-25-2012 08:36 AM

Bash script for printing days in a specific month (taking into account a leap year)
 
I have created a rather long winded bash script to print the number of days in the month, and give information about leap years if the current month is February. However I am having difficulty running it and need a better approach to scripting it if you can help? I am testing with if/elsif/else before trying a case function.

Code:

#!/bin/bash

# Leap year check

currentmonth=$(date +"%B")
month=$(date +%m)
leap=$(date +%Y)
M1=$01
M2=$02
M3=$03
M4=$04
M5=$05
M6=$06
M7=$07
M8=$08
M9=$09
M10=$10
M11=$11
M12=$12
M13=$13
echo "The current month is $currentmonth"
if [ $month -eq $M1 ]; then
    echo "$month has 31 days"
        elif [ $month -eq $M2 ]; then
            echo "$month has 28 days"
        elif [ $month -eq $M3 ]; then
            echo "$month has 31 days"
        elif [ $month -eq $M4 ]; then
            echo "$month has 30 days"
        elif [ $month -eq $M5 ]; then
            echo "$month has 31 days"
        elif [ $month -eq $M6 ]; then
            echo "$month has 30 days"
        elif [ $month -eq $M7 ]; then
            echo "$month has 31 days"
        elif [ $month -eq $M8 ]; then
            echo "$month has 31 days"
        elif [ $month -eq $M9 ]; then
            echo "$month has 30 days"
        elif [ $month -eq $M10 ]; then
            echo "$month has 31 days"
        elif [ $month -eq $M11 ]; then
            echo "$month has 30 days"
        elif [ $month -eq $M12 ]; then
            echo "$month has 31 days"
else [ $[$leap % 400] -eq "0" ]; then
    echo "This is a leap year. February has 29 days"
fi


MensaWater 04-25-2012 08:46 AM

You can use "cal" to get the calendar for any month. In your script you're apparently only interested in the current month so the following would get number of days for current month:

Code:

cal |egrep -v [a-z] |wc -w
The cal command by default shows calendar for current month. The egrep -v tells it to exclude the output for any line that contains letters which will be the Month and Day headers so you're left only with the numeric days. wc -w counts the words which will be the number of days in this case.

You can also specify a given month/year with cal. So if you wanted to see if February 2012 was a leap year you could do:
Code:

cal 2 2012 |egrep -v [a-z] |wc -w
You can even get the number of days in a year by specifying only the year (not month) with cal:
Code:

cal 2012 |egrep -v "[a-z]|[0-9][0-9][0-9]" |wc -w
Note the extra characters in the egrep are to exclude any 3 digit number so would exclude the year header (which is 4 digits) but leave all the days (which are 2 digits).

If interested only in the current year you can specify it with the date command:
Code:

cal $(date +%Y) |egrep -v "[a-z]|[0-9][0-9][0-9]" |wc -w

shayno90 04-25-2012 09:00 AM

Thanks for that MensaWater. I will use that in the future as much simpler. However I am practising some bash scripting with "if/else" statements and was looking for a simpler way to condense the script and achieve the same result as using "cal".

catkin 04-25-2012 09:03 AM

M1=$01 sets the value of M1 to the value of variable 0 with literal string 1 added to it. Try M1=01 etc.

catkin 04-25-2012 09:23 AM

  • You could make the script neater by doing away with the M* variables and dealing with the leap year in the February elif.
  • The variable leap actually holds the year so easier to understand if it has that name.
  • Although the months look like numbers they are actually strings so = is more appropriate than -eq.
  • [[ <test expression> ]] is preferred over [ <test expression> ] for reasons explained here.
  • (( <arithmetic expression> )) is arguably more appropriate for arithmetic testing than the traditional test forms.
  • I would line up the if-elif-else-fi but that is just a personal preference.
Putting that all together:
Code:

#!/bin/bash

# Leap year check

currentmonth=$(date +"%B")
month=$(date +%m)
year=$(date +%Y)
echo "The current month is $currentmonth"
if [[ $month = 01 ]]; then
    echo "$month has 31 days"
elif [[ $month = 02 ]]; then
    if (( year % 400 != 0 )); then
        echo "$month has 28 days"
    else
        echo "$month has 29 days"
    fi
elif [[ $month -eq 03 ]]; then
    echo "$month has 31 days"
...
elif [[ $month = 12 ]]; then
    echo "$month has 31 days"
else
    echo "Programming error: \$month not in expected 01-012 range: $month" >&2
fi


shayno90 04-25-2012 09:54 AM

Thanks for that info catkin as I needed to some assistance in trying to formulate an approach to scripting this issue.
Code:

#!/bin/bash

# Leap year check

currentmonth=$(date +%B)
month=$(date +%m)
year=$(date +%Y)
echo "The current month is $currentmonth"

if [[ $month = 01 ]]; then
    echo "$month has 31 days"
elif [[ $month = 02 ]]; then
    if (( $year % 400 != 0 )); then
        echo "$month has 28 days"
    else
        echo "$month has 29 days"
    fi           
elif [[ $month = 03 ]]; then
    echo "$month has 31 days"
elif [[ $month = 04 ]]; then
    echo "$month has 30 days"
elif [[ $month = 05 ]]; then
    echo "$month has 31 days"
elif [[ $month = 06 ]]; then
    echo "$month has 30 days"
elif [[ $month = 07 ]]; then
    echo "$month has 31 days"
elif [[ $month = 08 ]]; then
    echo "$month has 31 days"
elif [[ $month = 09 ]]; then
    echo "$month has 30 days"
elif [[ $month = 10 ]]; then
    echo "$month has 31 days"
elif [[ $month = 11 ]]; then
    echo "$month has 30 days"
elif [[ $month = 12 ]]; then
    echo "$month has 31 days"
else
    echo "Programming error: \$month not in expected 01-12 range: $month" >&2
fi

Not correct yet as:
The current month is April
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
Programming error: $month not in expected 01-12 range: 04

It has a problem with the final "fi" in the script.

grail 04-25-2012 09:55 AM

Personally I would change the ifs into a case to make it cleaner looking:
Code:

#!/bin/bash

currentmonth=$(date +%B)
month=$(date +%m)
year=$(date +%Y)

case $month in
    0[13578]|10|12) days=31;;
    0[469]|11)            days=30;;
    *)        (( year % 400 )) && days=29 || days=28
esac

echo "$month has $days days"


grail 04-25-2012 09:56 AM

No you need to check your typing:
Code:

if (( $year % 400] != 0 )); then

catkin 04-25-2012 09:59 AM

@grail: the OP is practising with if-elif-else-fi before moving on to case. And well spotted on that typo :)

shayno90 04-25-2012 10:01 AM

Quote:

Originally Posted by grail (Post 4662771)
No you need to check your typing:
Code:

if (( $year % 400] != 0 )); then

Yes, I noticed this a few minutes ago and changed it but got the same result.
I wanted to try "if/else" statements before moving on to "case" statements.

michaelk 04-25-2012 10:16 AM

FYI leap year check If I remember correctly is:
if(year%400 ==0 || (year%100 != 0 && year%4 == 0))

shayno90 04-25-2012 10:27 AM

Quote:

Originally Posted by michaelk (Post 4662789)
FYI leap year check If I remember correctly is:
if(year%400 ==0 || (year%100 != 0 && year%4 == 0))

Changing to
Code:

if ( $year % 400 == 0 ) || ($year % 100 != 0 && $year % 4 == 0); then
or
Code:

if ( ($year % 400 == 0 ) || ($year % 100 != 0 && $year % 4 == 0) ); then

gives the same output error:
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
leaptest.sh: 40: [[: not found
Programming error: 04 not in expected 01-12 range: 04

Line 40 is :
Code:

38 else
39    echo "Programming error: $month not in expected 01-12 range: $month" >&2
40 fi


michaelk 04-25-2012 10:52 AM

Sorry, I did not put the formula in proper bash syntax.
Code:

if (( ($year % 400) == 0 )) || (( ($year%4) == 0 ) && ($year%100) !=0) )); then
  echo "This is a leap year"
else
  echo "this is not a leap year"
fi


shayno90 04-25-2012 11:21 AM

Quote:

Originally Posted by michaelk (Post 4662809)
Sorry, I did not put the formula in proper bash syntax.
Code:

if (( ($year % 400) == 0 )) || (( ($year%4) == 0 ) && ($year%100) !=0) )); then
  echo "This is a leap year"
else
  echo "this is not a leap year"
fi


Syntax is not correct for
Code:

if (( ($year % 400) == 0 )) || (( ($year%4) == 0 ) && ($year%100) !=0) )); then
Syntax error: word unexpected (expecting ")")
The test expression brackets need to be changed.

The nested if/else statement for leap year is the issue as the syntax doesn't allow the script to run and results in previous error messages.

grail 04-25-2012 11:26 AM

Quote:

@grail: the OP is practising with if-elif-else-fi before moving on to case. And well spotted on that typo
hmmm ... can see a typo but not read the questions and answers :doh:

It would appear that the code in post #6 works just fine for me:
Code:

$ ./leap_year.sh
The current month is April
04 has 30 days

Is that the same way you are running the code?


All times are GMT -5. The time now is 12:54 PM.