2017-02-10 1 views
1

Ich arbeite daran, sich an Shell-Skripte zu gewöhnen und lief über ein Verhalten, das ich interessant und ungeklärt fand. Im folgenden Code wird die erste for-Schleife korrekt ausgeführt, die zweite dagegen nicht.Bash for-Schleife-Syntax

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 

Die zweite for-Schleife führt zu dem folgenden Fehler:

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

Was mich verwirrt ist, dass ${#letters[*]} eindeutig ausgewertet wird immer, richtig, auf die Nummer 7. Aber trotz der Code auch wenn nicht Wir haben gerade gesehen, dass die gleiche Schleife mit {0..7} völlig in Ordnung funktioniert.

Was ist der Grund dafür?

Ich verwende OS X 10.12.2, GNU Bash Version 3.2.57.

+0

Nicht sicher über die Erklärung für diese Aber wenn Sie {0..7} verwenden, wird dies auf erweitert 1 2 3 4 5 ... usw. Aber wenn Sie Sie {0 .. $ {# Buchstaben [* ]} dies erweitert sich zu einer Zeichenkette "{0..7}" Welche Sie nicht auf – Yarden

Antwort

3

Die Klammererweiterung findet vor der Parametererweiterung statt. Sie können also keine Variable als Teil eines Bereichs verwenden.

Erweitern Sie das Array in eine Liste von Werten:

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

Oder die Indizes des Arrays in eine Liste erweitern:

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

Wie in den Kommentaren erwähnt (danke), diese beiden Ansätze beherbergen auch Sparse-Arrays; Sie können nicht immer davon ausgehen, dass ein Array einen Wert für jeden Index zwischen und ${#letters[@]} definiert.

+1

iterieren können Diese beherbergen auch Sparse-Arrays; Sie können nicht immer davon ausgehen, dass ein Array einen Wert für * jeden * Index zwischen 0 und '$ {# letters [@]}' definiert. – chepner

+0

@chepner danke, editiert das in. –

5

Die Klammererweiterung findet vor der Parametererweiterung statt (siehe EXPANSIONS in man bash), daher funktioniert sie nur für Literale. Mit anderen Worten, Sie können die Klammererweiterung nicht mit Variablen verwenden.

Sie eine C-Stil Schleife verwenden kann:

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

oder einen externen Befehl wie seq:

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

Aber Sie in der Regel nicht erforderlich, die Indizes, anstatt Schleifen eines über die Elemente selbst, siehe @ TomFenechs Antwort unten. Er zeigt auch einen anderen Weg, die Liste der Indizes zu bekommen.

Beachten Sie, dass es {0..6}, nicht 7 sein sollte.