2017-02-10 157 views
1

我正在习惯使用shell脚本,并且遇到了一个我发现有趣和无法解释的行为。在下面的代码中,第一个for循环将正确执行,但第二个不会。Bash for循环语法

declare letters=(a b c d e f g) 
for i in {0..7}; do 
    echo ${letters[i]} 
done 
for i in {0..${#letters[*]}}; do 
    echo ${letters[i]} 
done 

以下错误第二个for循环的结果:

syntax error: operand expected (error token is "{0..7}") 

什么让我困惑的是,${#letters[*]}显然得到评估,正确,到7号。但尽管如此,代码失败,即使我们刚刚看到与{0..7}相同的循环完美无缺。

这是什么原因?

我正在运行OS X 10.12.2,GNU bash版本3.2.57。

+0

不确定关于此的解释 但是,当您使用{0..7}时,它将扩展为 1 2 3 4 5 ... etc 但是当您{0 .. $ {#letters [* ]}这扩展为一个字符串 “{0..7}”你不能迭代 – Yarden

回答

3

支架膨胀发生在参数展开之前,因此您不能将一个变量用作范围的一部分。

展开数组值的列表:

for letter in "${letters[@]}"; do 
    echo "$letter" 
done 

或者,展开数组的索引到一个列表:

for i in ${!letters[@]}; do 
    echo "${letters[i]}" 
done 

正如评论(感谢)提到,这两个方法也适应稀疏阵列;你不能总是假定数组定义了的值,每索引在0${#letters[@]}之间。

+1

这些也适应稀疏数组;你不能总是假设一个数组为0和'$ {#letters [@]}'之间的* every *索引定义一个值。 – chepner

+0

@chepner谢谢,编辑在。 –

5

括号扩展发生在参数扩展之前(请参见man bash中的EXPANSIONS),因此它仅适用于文字。换句话说,你不能在变量中使用大括号扩展。

可以使用C风格的循环:

for ((i=0; i<${#letters[@]}; i++)) ; do 
    echo ${letters[i]} 
done 

或外部命令状seq

for i in $(seq 1 ${#letters[@]}) ; do 
    echo ${letters[i-1]} 
done 

但你通常不需要索引,而不是一个遍历的元素他们自己,请参阅下面的@ TomFenech的答案。他还展示了获取指数列表的另一种方式。

请注意,它应该是{0..6}而不是7