简答:更改IFS 的影响是复杂且难以理解的,最好避免使用,除了一些定义明确的习语(IFS=, read ... 是我认为可以的习语之一)。
长答案:您需要牢记几件事,以便了解您从更改 IFS 中看到的结果:
鉴于上述情况,IFS=, read -a A 会按照您的预期进行,它将输入拆分为 ",":
$ IFS=, read -a A <<<"alpha,bravo,charlie"
$ declare -p A
declare -a A='([0]="alpha" [1]="bravo" [2]="charlie")'
但是echo 不注意;它总是在它传递的参数之间放置空格,因此使用IFS=something 作为它的前缀根本没有效果:
$ echo alpha bravo
alpha bravo
$ IFS=, echo alpha bravo
alpha bravo
所以当你使用IFS=, echo "${A[*]:1:2}" 时,它就等同于echo "${A[*]:1:2}",并且由于shell 对IFS 的定义以空格开头,所以它将A 的元素和它们之间的空格放在一起。所以相当于运行IFS=, echo "alpha bravo"。
另一方面,IFS=,; echo "${A[*]:1:2}" 更改了 shell 对 IFS 的定义,因此它确实影响了 shell 如何将元素组合在一起,因此它等同于 IFS=, echo "alpha,bravo"。不幸的是,从那时起,它还会影响其他所有内容,因此您必须将其隔离到子 shell 或之后将其恢复正常。
为了完整起见,这里有几个其他版本不起作用:
$ IFS=,; echo "${A[@]:1:2}"
bravo charlie
在这种情况下,[@] 告诉 shell 将数组的每个元素视为一个单独的参数,因此留给echo 来合并它们,它会忽略 IFS 并始终使用空格。
$ IFS=,; echo "${A[@]:1:2}"
bravo charlie
那么这个怎么样:
$ IFS=,; echo ${A[*]:1:2}
bravo charlie
在这种情况下,[*] 告诉 shell 将所有元素与它们之间的IFS 的第一个字符混合在一起,得到bravo,charlie。但它不在双引号中,因此 shell 立即将其重新拆分为 ",",再次将其拆分回单独的参数(然后 echo 一如既往地用空格将它们连接起来)。
如果您想更改IFS 的shell 定义而不必将其隔离到子shell,有几个选项可以更改它并在之后将其设置回来。在 bash 中,您可以像这样将其恢复正常:
$ IFS=,
$ while read -a A; do # Note: IFS change not needed here; it's already changed
> echo "${A[*]:1:2}"
> done <<<alpha,bravo,charlie
bravo,charlie
$ IFS=$' \t\n'
但$'...' 语法并非在所有shell 中都可用;如果您需要可移植性,最好使用文字字符:
IFS='
' # You can't see it, but there's a literal space and tab after the first '
有些人更喜欢使用unset IFS,它只是强制shell 执行其默认行为,这与以正常方式定义的IFS 几乎相同。
...但是如果IFS 已在更大的上下文中更改,并且您不想弄乱它,则需要保存它然后将其重新设置。如果它已正常更改,这将起作用:
saveIFS=$IFS
...
IFS=$saveIFS
...但是如果有人认为使用unset IFS 是个好主意,这会将其定义为空白,从而产生奇怪的结果。所以你可以使用这种方法或unset 方法,但不能同时使用。如果你想让它对抗unset 冲突,你可以在 bash 中使用这样的东西:
saveIFS=${IFS:-$' \t\n'}
...或者为了便于携带,请不要使用$' ' 并使用文字空格+制表符+换行符:
saveIFS=${IFS:-
} # Again, there's an invisible space and tab at the end of the first line
总而言之,对于粗心的人来说,这是一大堆充满陷阱的烂摊子。我建议尽可能避免它。