IFS-getrennte Elemente in Schleife

IFS-getrennte Elemente in Schleife

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 readBefehl 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=$sentenceskopiert einfach sentencesin sentences2, for sentence in ${sentences2[@]}ist also ein wenig seltsam. sentences2ist 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 2druckt "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 iWert "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 sentenceder 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, echodas 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.

verwandte Informationen