
Es wird empfohlen, IFS zu verwenden, um durch Komma-getrennte Listen zu schleifen, z. B.Hier. Aber ich sehe, dass
sentences="Hello World,Questions"
IFS=, sentences1=($sentences) # if we comment this line then loop is fixed
sentences2=$sentences
for sentence in ${sentences2[@]};
do
for i in $(seq 1 2);
do
#This is fine - prints a new word every line
#echo $sentence
#This is fine - prints new number every line
#echo $i
echo $i $sentence
done
done
produziert
1
2 Hello World
1
2 Question
Anstatt
1 Hello World
2 Hello World
1 Question
2 Question
gewünscht. WAR EIN?
Antwort1
Das Hauptproblem besteht hier darin, dass die zweite Zeile, IFS=, sentences1=($sentences)
, IFS dauerhaft auf "," setzt; dies bedeutet, dass Sie im gesamten restlichen Skript ein seltsames und unerwartetes Analyseverhalten erhalten. Beachten Sie, dass IFS=, read ...
dies bei etwas wie nicht passieren würde, da die IFS-Zuweisung so behandelt wird, als würde sie nur auf den read
Befehl angewendet; aber in IFS=, sentences1=($sentences)
gibt es keinen echten Befehl, sondern nur Zuweisungen, sodass die Zuweisungen so behandelt werden, als würden sie auf die gesamte Shell angewendet.
Sehen wir uns also an, was im Rest des Skripts geschieht. sentences2=$sentences
kopiert einfach sentences
in sentences2
, for sentence in ${sentences2[@]}
ist also ein wenig seltsam. sentences2
ist kein Array (nur ein einfacher String), also [@]
bewirkt das Bit nichts, aber da IFS immer noch "," ist, wird es in "Hallo Welt" und "Fragen" "wortgetrennt" – d. h., Sie erhalten das richtige Ergebnis, aber aus dem falschen Grund.
(Hinweis: Wenn es sich um ein echtes Array handeln würde, müssten Sie es in Anführungszeichen setzen, um for sentence in "${sentences2[@]}"
zu verhindern, dass die Elemente in Wörter getrennt werden.)
Beim nächsten Befehl, for i in $(seq 1 2)
, beginnt das wahre Problem. seq 1 2
druckt "1[newline]2[newline]", die $( )
Konstruktion schneidet die nachfolgende neue Zeile ab und dann wird "1[newline]2" in Wörter aufgeteilt -- aber da hier kein Komma steht, wird es als ein Wort behandelt (das zufällig eine neue Zeile enthält). Daher wird die innere Schleife nur einmal ausgeführt, mit dem i
Wert "1[newline]2".
Die nächste Zeile, echo $i $sentence
, gibt "1[newline]2" aus, gefolgt von einem Leerzeichen und dem Inhalt von sentence
. Bei der ersten Iteration mit sentence
der Einstellung "Hello World" wird Folgendes ausgegeben:
1
2 Hello World
...durch den Zeilenumbruch in der Mitte sieht es so aus, als würden zwei Dinge gedruckt, aber in Wirklichkeit ist es nur eines, echo
das zufällig einen Zeilenumbruch enthält.
Also zwei wichtige Empfehlungen: Erstens, wenn Sie IFS ändern, ändern Sie es danach wieder zurück. Zweitens, setzen Sie Variablenreferenzen immer in doppelte Anführungszeichen, um unerwartete Worttrennungen (und Platzhaltererweiterungen) zu vermeiden. Hier ist, was ich für das Skript bekomme:
#!/bin/bash
sentences="Hello World,Questions"
saveIFS="$IFS"
IFS=, sentences1=($sentences)
IFS="$saveIFS" # Set IFS back to normal!
for sentence in "${sentences1[@]}"; do # Note double-quotes, and sentences1 is
an actual array
for i in $(seq 1 2); do # No double-quotes, we *want* seq's output to be spli
t
echo "$i" "$sentence" # Double-quotes for safety
done
done
Antwort2
Ich weiß nicht, was der Grund ist, aber irc://freenode/bash bietet die Lösung
while IFS=, read -ra items; do
for item in "${items[@]}"; do
for i in {1..3}; do
printf '%d %s\n' "$i" "$item"
done
done
done <<< "Hello World,Questions,Answers"
das richtig druckt.