Mistake in shell function to count even numbers
You just forgot to replace $#
with ($
)element
in the for
loop:
function nevens {
local sum=0
for element in $@; do
let evencheck=$(( element % 2 ))
if [[ $evencheck -eq 0 ]]; then
let sum=sum+1
fi
done
echo $sum
}
Now to test the function:
$ nevens 42 6 7 9 33
2
$ nevens 42 6 7 9 33 22
3
$ nevens {1..10..2} # 1 to 10 step 2 → odd numbers only
0
$ nevens {2..10..2} # 2 to 10 step 2 → five even numbers
5
@dessert has found the core problem, I'll give some code review:
- The shebang: There is no
/usr/bin/bash
in Ubuntu. It's/bin/bash
. It's good that you have declared
sum
local
, and avoided polluting the variable namespace outside the function. Additionally, you can declare it an integer variable using the-i
option:local -i sum=0
Always quote your variables (and parameters)! It's not necessary in this script, but a very good habit to get into:
for element in "$@" do
That said, you can omit the
in "$@"
here:for element do
When
in <something>
is not given, thefor
loop implicitly loops over the arguments. This can avoid mistakes like forgetting the quotes.There's no need to calculate and then check the result. You can directly do the calculation in the
if
:if (( (element % 2) == 0 )) then ((sum = sum + 1)) fi
(( ... ))
is the arithmetic context. It's more useful than[[ ... ]]
for performing arithmetic checks, and additionally you can omit the$
before variables (which makes it easier to read, IMHO).If you moved the even-checking part into a separate function, it might improve readability and reusability:
function evencheck { return $(( $1 % 2 )) } function nevens { local -i sum=0 for element do # `if` implicitly checks that the returned value/exit status is 0 if evencheck "$element" then (( sum++ )) fi done echo "$sum" }
I'm not sure if you're open to other solutions. Also I don't know if you can use external utilities, or if you're purely limited to bash builtins. If you can use grep
, for example, your function could be a whole lot simpler:
function nevens {
printf "%s\n" "$@" | grep -c '[02468]$'
}
This puts each input integer on its own line, and then uses grep
to count the lines that end in an even digit.
Update - @PeterCordes pointed out that we can even do this without grep - just pure bash, so long as the input list contains just well formed integers (with no decimal points):
function nevens{
evens=( ${@/%*[13579]/} )
echo "${#evens[@]}"
}
This works by creating a list called evens
by filtering out all the odds, then returning the length of that list.