引用 bash 数组值时 [@] 和 [*] 有什么区别?

引用 bash 数组值时 [@] 和 [*] 有什么区别?

这个 Bash 指南说:

如果索引号为@*,则引用数组的所有成员。

当我这样做时:

LIST=(1 2 3)
for i in "${LIST[@]}"; do
  echo "example.$i"
done

它给出了所需的结果:

example.1
example.2
example.3

但是当我使用时${LIST[*]},我得到

example.1 2 3

反而。

为什么?

编辑:当使用printf, 时,@实际上*确实给出了相同的结果。

答案1

差异是微妙的;"${LIST[*]}"(like "$*") 创建一个参数,而"${LIST[@]}"(like "$@") 会将每一项扩展为单独的参数,因此:

LIST=(1 2 3)
for i in "${LIST[@]}"; do
    echo "example.$i"
done

将把列表作为多个变量来处理(打印它)。

但:

LIST=(1 2 3)
for i in "${LIST[*]}"; do
    echo "example.$i"
done

会将列表作为一个变量来处理。

答案2

使用[*]将创建单个字符串,数组的每个元素都与$IFS元素之间的第一个字符(默认为空格)连接起来。

使用[@]将创建一个列表

例子:

创建数组:

$ list=( a b "big fish" c d )

单独打印每个元素:

$ printf 'data: ---%s---\n' "${list[@]}"
data: ---a---
data: ---b---
data: ---big fish---
data: ---c---
data: ---d---

创建单个字符串并打印:

$ printf 'data: ---%s---\n' "${list[*]}"
data: ---a b big fish c d---

再次,但使用自定义分隔符:

$ IFS='/'
$ printf 'data: ---%s---\n' "${list[*]}"
data: ---a/b/big fish/c/d---

请注意,使用这些扩展没有双引号很少有意义。


如果 的第一个字符是多字节字符,或者是无法解码为字符的字符,则如果$IFS未设置(在之后IFS=),则如果$IFS未设置(在之后),则没有任何内容,但 shell 之间存在一些差异。unset -v IFS$IFS

答案3

考虑列表元素不包含特殊字符(例如空格)的简单情况。然后可以删除引号。那么@和*会给出相同的结果。

LIST=(1 2 3)
for i in ${LIST[*]}; do
  echo "example.$i"
done

输出:

example.1
example.2
example.3

现在考虑元素包含空格的情况:

LIST=(1 "a b" 3)
for i in ${LIST[*]}; do
  echo "example.$i"
done

在这种情况下,@和*仍然给出相同的结果:

example.1
example.a
example.b
example.3

但这不是我们想要的,因为“a b”被分割为两个元素而不是一个元素。所以我们需要使用引号来防止这种情况:

LIST=(1 "a b" 3)
for i in "${LIST[*]}"; do
  echo "example.$i"
done

但结果是example.1 a b 3。接下来我们将*改为@:

LIST=(1 "a b" 3)
for i in "${LIST[@]}"; do
  echo "example.$i"
done

最终我们得到了想要的结果:

example.1
example.a b
example.3

总结上述结果,我们看到 @ 在被某些运算符(例如引号)操作时允许逐元素操作。

相关内容