We can just write a function that iterates through the digits of the mask, adding up the number of bits set in each digit. There are only five legal digits in a CIDR-compliant netmask.
Bash has the ${variable:offset}
and ${variable:offset:length}
substitutions for getting substrings of a variable, so it's pretty easy to pick off the digits one at a time.
function bitCountForMask {
local -i count=0
local mask="${1##0x}"
local digit
while [ "$mask" != "" ]; do
digit="${mask:0:1}"
mask="${mask:1}"
case "$digit" in
[fF]) count=count+4 ;;
[eE]) count=count+3 ;;
[cC]) count=count+2 ;;
8) count=count+1 ;;
0) ;;
*)
echo 1>&2 "error: illegal digit $digit in netmask"
return 1
;;
esac
done
echo $count
}