神秘的IFS行为

4

在这里https://dev59.com/JGIj5IYBdhLWcg3wwHtr#19915925我了解到可以通过设置IFS将字符串拆分为数组。

在这里https://dev59.com/Q3I_5IYBdhLWcg3wF_F3#9429887我了解到可以通过IFS分隔符来连接数组。

但是在我的下面测试中:

0:~ $ a=(1 2 3)
0:~ $ echo "${a[*]}"
1 2 3
0:~ $ IFS=. echo "${a[*]}" # IFS=. not work
1 2 3
0:~ $ (IFS=.; echo "${a[*]}") # this works
1.2.3
0:~ $ echo $IFS # the original IFS is not change

0:~ $ v=1.2.3
0:~ $ IFS=. b=($v) # change string to array
0:~ $ echo ${b[*]}
1 2 3
0:~ $ echo "${b[*]}" # the array join by `.`!
1.2.3
0:~ $ echo ${b}
1
0:~ $ (IFS=,; echo "${b[*]}") # this still work
1,2,3
0:~ $ IFS=, echo "${b[*]}" # this not work, b array still join by .
1.2.3
0:~ $ c=(1 2 3)
0:~ $ echo "${c[*]}" # a new array join by '.' !
1.2.3
0:~ $ IFS=, echo "${c[*]}" # IFS=, not work, still join by '.'
1.2.3
0:~ $ (IFS=,; echo "${c[*]}") # this works
1,2,3
0:~ $ echo $IFS # original IFS is space

0:~ $ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.

从上面的内容中,我有以下的猜想/问题:

  1. IFS=. echo "${a[*]}" 这个命令改变了echo环境,但没有改变引用扩展环境,所以它不起作用了吗?
  2. 如果1是正确的,为什么 IFS=. b=($v) 能够成功创建一个数组?
  3. 为什么 echo "${c[*]}" 会通过"."连接? IFS应该是默认的空格。

IFS=,echo "${a[*]}"正在被Shell作为一个命令中断,这就是为什么它不能工作的原因。将其拆分为两个命令:首先是 IFS=.,然后是一个 echo 命令,这样它就能正常工作了。 - gregory
1
https://www.gnu.org/software/bash/manual/html_node/Simple-Command-Expansion.html 加上 a=b cmd 仅在 cmd 运行期间设置并导出 a,但 a=b c=d 永久设置 a 和 c。注意,即使已设置 IFS,echo $IFS 始终为空,因为 $IFS 的扩展使用相同的字符“分割”,不会留下任何字符供 echo 使用;使用 echo "$IFS" 或更好地使用 printf '%s\n' "$IFS",或者可能使用 declare -p IFS - dave_thompson_085
1个回答

2
问题出在当你执行IFS=. b=($v)时,IFS设置的值会在当前shell环境中进行两个shell赋值。将IFS修改为.将使所有后续的数组展开类型"${arr[*]}"使用这个新值。注意这里的双引号,没有引号的数组展开不会使用IFS,即${arr[*]}
所以回答你的问题:

IFS=. echo "${a[*]}"这样改变echo的环境,但不改变引用展开的环境,所以它不起作用?

你错了,尽管你将IFS传递给了echo接收的环境,但是echo并不对其环境列表做任何事情。结果之所以发生是因为你之前已经将IFS的值设置为.

如果1是真的,为什么IFS=. b=($v)可以成功创建数组?

这个命令的运行就像你分别执行两个命令一样。将IFS修改为.,然后将未加引号的字符串v的结果展开成一个数组,该数组通过IFS的值进行单词拆分。由于之前你定义了IFS.,所以该字符串被拆分成单个组件并存储到数组中。

为什么echo "${c[*]}"会用.连接?IFS应该是默认的空格。

这在第一个答案中已经解释过了。你已经在当前shell中修改了使用IFS.,后续的数组展开将采用这些内容来连接字符串。如果你启动一个新的shell并运行相同的展开,它将不起作用。
只是想再澄清一点,关于何时将变量传递给命令的本地环境会起作用。如果不是使用export将变量导出到全局环境,则只有在当前shell环境中运行命令时才能访问该变量。
var=val echo "$var"

如果你已经写了,最初的回答。
var=val sh -c 'echo "$var"'

你看到了写入var的值,因为在这种情况下,你正在传递环境中的var值,运行sh shell。但与echo不同,sh从环境中读取并查看var的值,并将其用于变量扩展。"最初的回答"

谢谢!我没有注意到IFS=. b=($v)设置了两个变量,而不是作为临时环境。 - SolaWing

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