BASH Grouping tests in IF statement

Ok I got the following to work by removing the parentheses arount $year and it works. Can a kind soul please explain this behavior. Also, to my topic question, am I using curly braces correctly in the IF statement? The intent is to check for a leap year. If the year is divisible by 400 then no need to test further. If not, then the year must be divisible by 4 AND not be divisible by 100.

#!/bin/bash

# get year from user's terminal

echo "Type the year that you would like to test (4 digits), followed by [ENTER]:"

read year

if [[ $(( "$year" % 400 )) -eq "0" ]]  || { [[ $(( "$year" % 4 )) -eq "0" ]] && [[ $(( "$year" % 100 )) -ne "0" ]] ; } ;then
   echo "$year is a leap year"
else
   echo "$year is not a leap year"
fi

Ok I got it working by removing the parentheses. Why can I not quote a variable in an arithmetic expression?

It isn't immediately obvious to me why bash complains about your provided code. (But, I usually use ksh instead of bash .) A recent ksh runs it without complaining.

Both bash and ksh are happy with:

#!/bin/bash

# get year from user's terminal

echo "Type the year that you would like to test (4 digits), followed by [ENTER]:"

read year

if [[ $((year % 400)) == 0 || ( $((year % 4)) == 0 && $((year % 100)) != 0 ) ]]
then
   echo "$year is a leap year"
else
   echo "$year is not a leap year"
fi
1 Like

Firstly thank you. Your code looks much better than mine. Why do you use double equals "==" ? What would be the consequences of using a single equals sign? Thanks in advance

The correct syntax with test expr and [ expr ] for expressions performing numeric comparisons is -lt , -le , -eq , -ne , -gt , or -ge and for string comparisons is = and != .

With [[ expr ]] , the preferred operators for string and numeric comparisons are < , <= , == , != , >= , and > , but any of the forms used with test and [ ] can also be used. For [[ ]] , = is deprecated and == is preferred.

Depending on what shell you're using, == may be accepted as a synonym for = in test and [ ] , but == is not specified by the standards and does not work portably.

1 Like

Thank you for explaining that. Marking as solved.

You should notice that inside a $((...)) the expression is treated as if it were quoted and the variables may be referred by name without using the `$' symbol.

if [[ $((year % 400)) == 0 || ( $((year % 4)) == 0 && $((year % 100)) != 0 ) ]]

In terms of efficiency, meaning to reduce the number of tests, I'd test the most probable one first, i.e. year % 4 , which becomes 0 every fourth year, as opposed to year % 400 , which becomes 0 every 400th year only (and not for the next 385 years., btw). So in the vast majority of cases, you'll be off with one single test only.