3

I have a simple script:

is_prime () {
    local factors
    factors=( $( factor "$1" | cut -d ':' -f 2 ) )
    [ ${#factors} -le 1 ]
}

starting () {
    for i in $(seq 10 99); do
        if is_prime "$i" ; then echo "$i" ; fi
    done
}


starting

If I run it with /bin/zsh, it prints all the primes between 10 and 99, as I would expect. If I run it with /bin/bash, however, it does the exact opposite: it prints all non-prime values in that range! I understand zshs behaviour, but why does bash do what it does? Is the behaviour of test different in some relevant way?

2
  • 3
    BTW, consider for ((i=10; i<99; i++)) instead of for i in $(seq ...); this keeps evaluation internal to the shell rather than launching a nonstandardized external tool. (I've actually seen someone build an implementation of seq that spelled out numbers just to mess with people -- one, two, etc; there's no POSIX standard for it, so it's perfectly legal for it to do weird things, or simply not be there at all). Commented Feb 18, 2015 at 16:28
  • You could also get rid of cut: factor_str=$(factor "$1"); factor_str=${factor_str#*:}; read -A factors <<<"$factor_str" (in zsh), or read -a factors <<<"$factor_str" (in bash). Frankly, they're different enough shells that it assuming that things will work between them isn't a great habit to be in. Commented Feb 18, 2015 at 16:31

1 Answer 1

3

Apparently in zsh this ${#factors} expansion on an array gets you the length of the array. It doesn't do that in bash. In bash that gets you the length of the first element of the array.

Compare these outputs:

$ zsh -c 'foo=(a b c); echo $foo'
a b c
$ bash -c 'foo=(a b c); echo $foo'
a
$ zsh -c 'foo=(a b c); echo ${#foo}'
3
$ bash -c 'foo=(a b c); echo ${#foo}'
1

So that's why your script is going wrong for bash. To get the value you expected there you need to use ${#factors[@]} (which also works in zsh).

$ zsh -c 'foo=(a b c); echo ${#foo[@]}'
3
$ bash -c 'foo=(a b c); echo ${#foo[@]}'
3
Sign up to request clarification or add additional context in comments.

2 Comments

set -x is a very useful tool. =)
Aha! And because all non-prime numbers less than 100 have a single-digit factor, it was just a funny accident that bash was printing all and only the composite numbers! Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.