'Function in shell script bad math expression

I have tried a program using function. My code is:

#!/bin/zsh

odd=()
prime=()

isprime()
{
    for (( i=2; i<=`expr sqrt($1)`; i++))
    do
        if [ `expr $1 % $i` -eq 0 ]
        then
            c=`expr $c + 1`
        fi
    done
    if [ $c -eq 0 ]
    then
        prime[$1]=$1
    fi
}

echo -n "Even = "
for (( i=$1; i<=$2; i++))
do
    if [ `expr $i % 2` -eq 0 ]
    then
        echo -n "$i "
    else
        odd[$i]=$i
    fi
    isprime $i
done

echo
echo "Odd = ${odd[*]}"
echo "Prime = ${prime[*]}"

On execution I get the following error message:

Even = 2 isprime:2: unknown file attribute: 2
isprime:2: bad math expression: operand expected at end of string

Can someone please tell me what is wrong with this code?



Solution 1:[1]

There's a bunch of issues:

The variable i being used inside the function without being declared as local is the same as the i variable outside the function, setting you up for an infinite loop.

You're using expr blah instead of using arithmetic expansion $((blah)) or ((blah)) in many places. Inefficient, and expr doesn't understand syntax like sqrt(X).

If the first loop in isprime never executes the body, the variable c is never set, causing the second error.

I can't replicate the first error message you're getting on a few different versions of zsh, but I suspect it's from sqrt(2) undergoing filename expansion and the glob qualifier 2 not existing. I just can't find the right set of options to make that particular error message.

Rewritten into pure zsh, you get:

#!/usr/bin/env zsh

zmodload zsh/mathfunc # For sqrt()
typeset -a odd prime

isprime()
{
    local -i i c=0 # Local integer variables
    for (( i=2; i<=sqrt($1); i++ ))
    do
        if (( $1 % i == 0 )) # Use arithmetic evaluation instead of [ + expr
        then
            c=$((c + 1)) # Arithmetic substitution that evaluates to the result
        fi
    done
    if (( c == 0 )) # Can also be [[ $c -eq 0 ]]
    then
        prime[$1]=$1
    fi
}

echo -n "Even = "
for (( i=$1; i<=$2; i++ ))
do
    if (( i % 2 == 0 ))
    then
        echo -n "$i "
    else
        odd[$i]=$i
    fi
    isprime $i
done

echo
echo "Odd = ${odd[*]}"
echo "Prime = ${prime[*]}"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shawn