a="$@" 的意外结果

a="$@" 的意外结果

我正在与这种情况作斗争:

$ set -- 1 2 3
$ a="$@"
$ echo "$a"
1 2 3

让我意想不到的是作业本身。

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

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

所以这应该类似于:

b="1" "2" "3"
bash: 2: command not found

我猜这就是"$*"扩展的目的:

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

所以这应该是正确的:

$ set -- 1 2 3
$ a="$*"
$ echo "$a"
1 2 3

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

外壳检查将此检测为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

答案1

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

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

但,

在所有其他情况下,扩展的结果是未指定

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


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

"$@"正如该 shellcheck 页面所述,不同 shell 的赋值行为有所不同。 Bash 和 ksh 用空格连接位置参数,zsh 和 dash 用 的第一个字母连接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>

a="$*"如果您想连接到单个字符串,或者以其他方式明确编写您想要的内容,那么最好使用它。

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

相关内容