Reputation: 6015
I'm trying to refactor this code:
if [ $(($1 % 4)) -eq 0 ] && [ $(($1 % 100)) -ne 0 ] || [ $(($1 % 400)) -eq 0 ] ; then
echo $T
else
echo $F
fi
into something like this:
if divisibleBy4 && notDivisibleBy100 || divisibleBy400; then
echo $T
else
echo $F
fi
note that
T="true"
F="false"
divisibleBy4 function looks like:
divisibleBy4() {
return [ $(($1 % 4)) -eq 0 ]
}
But I've tried several iterations including what I thought would definitely work.
divisibleBy4() {
if [ $(($1 % 4)) -eq 0 ]; then
return 1
else return 0
fi
}
Any idea how to properly fix the syntax so I can refactor these into functions and use them in my if statement?
When testing I'm seeing the error
syntax error: operand expected (error token is "% 4")
Another thing I tried is, but still doesn't seem to work:
INPUT=$1
divisibleBy4() {
if [ $(($INPUT % 4)) -eq 0 ]; then
return 1
else return 0
fi
}
notDivisibleBy100() {
if [ $(($INPUT % 100)) -ne 0]; then
return 1
else return 0
fi
}
divisibleBy400() {
if [ $(($INPUT % 400)) -eq 0 ]; then
return 1
else return 0
fi
}
if divisibleBy4 && notDivisibleBy100 || divisibleBy400; then
echo $T
else
echo $F
fi
or
INPUT=$1
divisibleBy4() {
return $((!($INPUT %4)))
}
notDivisibleBy100() {
return $(($INPUT %100))
}
divisibleBy400() {
return $((!($INPUT %400)))
}
(( divisibleBy4 && notDivisibleBy100 || divisibleBy400 )) && echo "true" || echo "false"
Upvotes: 0
Views: 2548
Reputation: 189936
Every command sets a result code. If you want to force each calculation to happen in a separate function, you can say
divisibleBy4() {
$((!("$1" % 4)))
}
notDivisibleBy100() {
$(("$1" %100))
}
divisibleBy400() {
$((!("$1" %400)))
}
(divisibleBy4 "$1" &&
notDivisibleBy100 "$1" ||
divisibleBy400 "$1") &&
echo "true" || echo "false"
Breaking up your logic to functions on the subatomic level is not really helping legibility and maintainability, though. Perhaps if you want to make each part reasonably self-domumenting, use comments.
is_leap () {
# Divisible by 4?
(("$1" % 4 == 0)) || return
# Not divisible by 100?
(("$1" % 100 > 0)) && return
# Divisible by 400?
(("$1" % 400 == 0))
}
... Though the comments seem rather superfluous here.
Upvotes: 0
Reputation: 126108
The simplest, directest answer is to just create functions that consist only of the tests themselves:
INPUT=$1
divisibleBy4() {
[ $(($INPUT % 4)) -eq 0 ]
}
notDivisibleBy100() {
[ $(($INPUT % 100)) -ne 0 ]
}
divisibleBy400() {
[ $(($INPUT % 400)) -eq 0 ]
}
The reason this works is that a function without a return
will implicitly return the status of the last command in the function; in these cases, that's the test command (note: [
is a command, even though it doesn't look like one), so the functions just return the result of the test directly.
I'd make at least one change to these, though: they all test the value of the shell variable INPUT
; it's much better practice to actually pass the data that functions operate on as parameters. Thus, it'd be better to do something like this:
divisibleBy4() {
[ $(($1 % 4)) -eq 0 ]
}
if divisibleBy4 "$1" ...
Rather than this:
divisibleBy4() {
[ $(($INPUT % 4)) -eq 0 ]
}
INPUT=$1
if divisibleBy4 ...
Note that you can also bundle up the whole leap year check the same way:
isLeapYear() {
[ $(($1 % 4)) -eq 0 ] && [ $(($1 % 100)) -ne 0 ] || [ $(($1 % 400)) -eq 0 ]
}
if isLeapYear "$1"; then
Or use the simpler form @Wiimm suggested:
isLeapYear() {
(( !($1%4) && $1%100 || !($1%400) ))
}
Also, for the shell variables you do use, lower- or mixed-case is preferred, to avoid accidental conflicts with the many all-caps variable names that have special meanings or functions.
Upvotes: 1
Reputation: 3615
You want to detect a leap year! A complete other solution using math mode directly:
a="$1"; (( !(a%4) && a%100 || !(a%400) )) && echo true || echo false
or as if-then-else
a="$1";
if (( !(a%4) && a%100 || !(a%400) )); then
echo true
else
echo false
Upvotes: 3