ループ内のIFSで区切られた項目

ループ内のIFSで区切られた項目

カンマ区切りのリストをループするにはIFSを使用することをお勧めします。例:ここしかし、私は

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

生産する

1 
2 Hello World
1
2 Question

の代わりに

1 Hello World
2 Hello World
1 Question 
2 Question

望ましい。でした?

答え1

ここでの主な問題は、2 行目の がIFS=, sentences1=($sentences)IFS を "," に永続的に設定することです。つまり、スクリプトの残りの部分全体で奇妙で予期しない解析動作が発生します。 のようなものではIFS=, read ...、IFS 割り当てがコマンドにのみ適用されるものとして扱われるため、このようなことは起こりませんreadIFS=, sentences1=($sentences)、 では実際のコマンドはなく割り当てだけであるため、割り当てはシェル全体に適用されるものとして扱われます。

では、スクリプトの残りの部分で何が起きるかを見てみましょう。は にsentences2=$sentencesコピーするだけなので、少し奇妙です。は配列ではない (単なるプレーンな文字列) ので、この部分は何もしませんが、IFS は依然として "," なので、"Hello World" と "Questions" に "単語分割" されます。つまり、正しい結果が得られますが、その理由は間違っています。sentencessentences2for sentence in ${sentences2[@]}sentences2[@]

for sentence in "${sentences2[@]}"(注: 実際の配列の場合は、要素が単語分割されないように、二重引用符で囲む必要があります。)

次のコマンドfor i in $(seq 1 2)で、本当の問題が始まります。seq 1 2は「1[newline]2[newline]」を出力し、$( )構文によって末尾の改行がトリミングされ、「1[newline]2」が単語分割されますが、そこにはコンマがないため、1 つの単語 (たまたま改行を含む) として扱われます。したがって、内側のループは がi「1[newline]2」に設定されて 1 回だけ実行されます。

次の行 は、echo $i $sentence「1[改行]2」の後にスペースが続き、 のコンテンツが出力されますsentence。 が「Hello World」に設定された最初の反復では、sentence次のように出力されます。

1 
2 Hello World

...真ん中の改行により、2 つのものが印刷されているように見えますが、実際には、echoたまたま改行が含まれているのは 1 つだけです。

そこで、2 つの大きな推奨事項があります。まず、IFS を変更したら、後で元に戻します。次に、予期しない単語の分割 (およびワイルドカードの拡張) を回避するために、変数参照を常に二重引用符で囲みます。スクリプトの結果は次のとおりです。

#!/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

答え2

理由はわかりませんが、irc://freenode/bash が解決策を提供しています

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"

正しく印刷されます。

関連情報