循环中以 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

这里的主要问题是第二行,IFS=, sentences1=($sentences)将 IFS 永久设置为“,”;这意味着您在脚本的其余部分会遇到奇怪且意外的解析行为。请注意,对于类似 的情况IFS=, read ...,这种情况不会发生,因为 IFS 赋值被视为仅适用于read命令;但在 中IFS=, sentences1=($sentences),没有真正的命令,只有赋值,因此赋值被视为适用于整个 shell。

那么,让我们看看脚本其余部分发生了什么。sentences2=$sentences只是复制sentencessentences2,所以for sentence in ${sentences2[@]}有点奇怪。sentences2不是一个数组(只是一个简单的字符串),所以这个[@]位没有做任何事情,但由于 IFS 仍然是“,”,它会被“分割”成“Hello World”和“Questions”——即你得到了正确的结果,但却是出于错误的原因。

(注意:如果它是一个实际的数组,则需要在其周围加上双引号,以for sentence in "${sentences2[@]}"防止元素被分割。)

下一个命令for i in $(seq 1 2),是真正的麻烦开始的地方。seq 1 2打印“1[newline]2[newline]”,构造$( )修剪尾随的换行符,然后“1[newline]2”进行分词——但由于那里没有逗号,它被视为一个单词(恰好包含换行符)。因此,内部循环只运行一次,设置为i“1[newline]2”。

下一行,echo $i $sentence打印“1[newline]2”,后面跟着一个空格,然后是的内容sentence。在第一次迭代中,sentence设置为“Hello World”,这将打印:

1 
2 Hello World

...中间的换行符使得这看起来像是打印了两个东西,但实际上只是一个echo恰好包含换行符的东西。

因此,有两个重要建议:首先,当您更改 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"

可以正确打印。

相关内容