2017-02-10 156 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