为单个语句设置IFS

5
在我的GNU bash版本为4.3.42(1)-release的环境中,我正在进行一些测试来回答一个问题。这个想法是将一个以冒号分隔的字符串拆分成一个数组,然后对其每个元素进行操作。
为此,我尝试在命令的范围内将IFS设置为:,以便自动拆分并保持IFS不变:
$ myvar="a:b:c"
$ IFS=: d=($myvar)
$ printf "%s\n" ${d[@]}
a
b
c

显然 IFS 保持不变:

$ echo $IFS
                      # 空的

BASH reference 声明如下:

如果未设置 IFS,则参数将以空格分隔。如果 IFS 为空,则参数将连接在一起而没有分隔符。

然而,我注意到 IFS 有点损坏,因此 echo $myvar 返回 a b c 而不是 a:b:c

取消设置值即可解决问题:

$ unset IFS
$ echo $myvar
a:b:c

但我想知道:是什么导致了这种情况?难道 IFS=: command 只在执行命令的范围内改变了 IFS 吗?

我在 为单个语句设置IFS 中看到确实可以这样做:

$ IFS=: eval 'd=($myvar)'
$ echo $myvar
a:b:c

但我不明白为什么它会生效,而IFS=: d=($myvar)则不会。
3个回答

8

当我看到你使用它时,我本来想对此发表评论的,但直到现在我才意识到问题所在。这行代码:

IFS=: d=($myvar)

它并不是临时设置IFS,而只是在当前shell中设置了两个变量。(简单命令可以加上本地环境设置,但赋值语句本身不是一个简单命令。)

当你写下以下代码时:

echo $IFS

IFS扩展为:,但由于:IFS的第一个字符,在单词拆分期间被移除。使用

echo "$IFS"

会显示 IFS 仍然设置为 :


1
关于echo $IFSecho "$IFS"之间的区别,你提出了一个很好的观点。正如每个人都说的那样,在bash中引用你的变量。 :-) - ghoti
1
最初让我感到困惑的是IFS的循环使用影响了$IFS的显示;我非常确定赋值语句没有环境,但我无法立即找出为什么echo $IFS似乎显示旧值。 :) - chepner
太好了,感谢您的回答并指出我为什么走错了路。 - fedorqui
更好的方法是:printf "%q\n" "$IFS",因为这样可以显示$IFS可能具有的空格值。 - dawg
@dawg 在这种情况下,我只是指出如何确认IFS的值中没有任何空格,而只有先前分配的: - chepner

4

IFS在同一行上与read一起使用时表现良好:

myvar="a:b:c"
IFS=: read -ra d <<< "$myvar"
printf "%s\n" "${d[@]}"
a
b
c

检查IFS的值:

declare -p IFS
-bash: declare: IFS: not found

很明显,在当前的shell中没有篡改IFS

现在检查原始输入:

echo $myvar
a:b:c

或者:

echo "$myvar"
a:b:c

1
有趣的内容,非常感谢anubhava。请注意其他帖子提到的问题:我的命令正在执行两个赋值操作。所以你建议的很聪明:我们现在只有一个[临时]赋值和一个命令,而不是两个赋值操作。 - fedorqui
1
一如既往,非常感谢您的友善回答,anubhava!很遗憾我不能接受这三个答案,因为它们都提供了非常有价值的信息。 - fedorqui
1
非常感谢您。您确实已将正确答案标记为已接受,因为@Chepner正确解释了问题的原因。 - anubhava

2
在bash中,如果一个语句本身不是变量赋值语句,你可以设置一个仅在该语句有效的变量。例如:
$ foo=one bar=two
$ echo $foo
one

要将第二个赋值语句作为语句的一部分,您需要...其他语句。正如您所注意到的,eval可以工作。此外,read也应该可以工作:
$ foo="one:two:three"
$ IFS=: read -r -a bar <<< "$foo"
$ declare -p bar
declare -a bar='([0]="one" [1]="two" [2]="three")'

请注意,此问题涉及需要符合POSIX标准的代码的问题,这就是为什么未考虑此处字符串的原因。 - chepner
啊,原来是这个问题,我不明白为什么“eval”会让它工作。 - fedorqui
再次感谢。可惜我不能接受这三个答案,因为我从中都学到了很多! - fedorqui
非常愉快。尽管我首先在另一个问题中诊断了这个问题,但chepner对这个问题的回答是唯一包含“简单命令”正确术语的答案。当然,anubhava不需要更多的分数,他已经获得了有史以来最高的分数。 :) - ghoti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接