Bash / Zsh脚本行为差异

3

我有一个简单的脚本:

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

如果我使用 /bin/zsh 运行它,它会打印出在 10 和 99 之间的所有质数,正如我所期望的那样。然而,如果我使用 /bin/bash 运行它,则恰恰相反:它打印出该范围内的所有非质数!我理解 zsh 的行为,但为什么 bash 会做出这样的行为?test 的行为方式是否有所不同?

3
顺便提一下,考虑使用for ((i=10; i<99; i++))而不是for i in $(seq ...);这会使评估在shell内部进行而不是启动一个非标准化的外部工具。 (我曾经见过有人构建了一个实现seq的程序,用单词拼出数字来恶作剧——“one”、“two”等等;因为没有POSIX标准指定它的行为,所以它可以做一些奇怪的事情,或者完全不存在)。 - Charles Duffy
你也可以摆脱 cut 命令:在 zsh 中使用 factor_str=$(factor "$1"); factor_str=${factor_str#*:}; read -A factors <<<"$factor_str",在 bash 中使用 read -a factors <<<"$factor_str"。老实说,它们是不同的 shell,假设它们之间的事情能够正常工作并不是一个好习惯。 - Charles Duffy
1个回答

3

显然,在zsh中,数组的${#factors}扩展可以得到数组的长度。但在bash中不是这样的。在bash中,这会得到数组的第一个元素的长度。

比较以下输出:

$ 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

因此,这就是为什么你的 bash 脚本出了问题。为了得到你期望的值,你需要使用 ${#factors[@]}(在 zsh 中也适用)。
$ zsh -c 'foo=(a b c); echo ${#foo[@]}'
3
$ bash -c 'foo=(a b c); echo ${#foo[@]}'
3

2
"set -x" 是一个非常有用的工具。 =) - Etan Reisner
啊哈!因为所有小于100的非质数都有一个一位数的因子,所以bash只打印了所有的合数,这只是一个有趣的巧合!谢谢。 - user2141650

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接