a="$@" 的意外结果

7 bash array string assignment shellcheck

我在这种情况下苦苦挣扎:

$ set -- 1 2 3
$ a="$@"
$ echo "$a"
1 2 3
Run Code Online (Sandbox Code Playgroud)

我觉得出乎意料的是作业本身。

man bash关于"$@"扩展是这样说的:

当扩展发生在双引号内时,每个参数都扩展为一个单独的词。

所以这应该类似于:

b="1" "2" "3"
bash: 2: command not found
Run Code Online (Sandbox Code Playgroud)

这就是"$*"扩展的目的,我收集:

当扩展发生在双引号内时,它扩展为单个单词,每个参数的值由 IFS 特殊变量的第一个字符分隔。也就是说,“$*”等价于“$1c$2c...”,其中c是IFS变量值的第一个字符。如果未设置 IFS,则参数以空格分隔。如果 IFS 为空,则连接参数时不插入分隔符。

所以这应该是正确的:

$ set -- 1 2 3
$ a="$*"
$ echo "$a"
1 2 3
Run Code Online (Sandbox Code Playgroud)

那么为什么"$@"产量相同呢?他们应该在这一点上有所不同。这是 Bash 问题还是我的误解?

Shellcheck 将其检测为SC2124。我还可以提供一个触发SC2145的示例。

观察到:

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02) x86_64 GNU/Linux
Run Code Online (Sandbox Code Playgroud)

ilk*_*chu 8

据我所知,POSIX$@在赋值中留下了 undefined,所以它并不是一个真正的错误,除了可能在 Bash 的文档中。$@在两种情况下定义:

  • 当扩展发生在将执行字段拆分的上下文中时......
  • 当扩展发生在双引号内时,行为是未指定的,除非 [...] 将执行字段拆分 [...] (*)

但,

在所有其他上下文中,未指定扩展的结果。

字段拆分不会在赋值中发生,即使没有双引号也不会发生,所以这是未定义的。


现在,我假设它的a="$@"行为方式与a="$*"因为扩展到多个单词在这里没有任何意义一样。您不能将多个单词作为不同的实体分配给常规变量,分配一个但使用其余单词作为命令参数会造成混淆并容易出错。

正如那个 shellcheck 页面所说,"$@"赋值中的行为在 shell 之间是不同的。Bash 和 ksh 将位置参数与空格、zsh 和破折号与首字母 of IFS(完全一样"$*")连接起来。

$ bash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ ksh93 -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ zsh -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>
$ dash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>
Run Code Online (Sandbox Code Playgroud)

a="$*"如果您想加入单个字符串,或者以其他方式明确写出您想要的内容,最好使用它。

(* 或其他涉及${parameter:-word}扩展的情况)