Bash 内置命令的 "a=b" 类参数中的文件名扩展

Filename expansion in "a=b"-like arguments of Bash built-in commands

我了解到当 运行 在 Bash 中执行命令时,文件名扩展是在命令执行之前完成的。但是当尝试下面的命令时(使用 -x 选项):

touch foo=3    # Create a file with name "foo=3"
+ touch foo=3
declare foo=?
+ declare 'foo=?'
alias foo=*
+ alias 'foo=*'

我没有得到我期望的结果,因为 foo=?并且 foo=* 未扩展为文件名 "foo=3":

declare -p | grep 'foo='    # => foo='?'
alias | grep 'foo='         # => alias foo='*'

但是如果我 运行 另一个像 cd 这样的内置函数或接受赋值作为我自己编写的参数的函数 show_rhs() { echo "${1%=*}='${1#*=}'"; } 我得到了我所期望的(foo=?和 foo=*展开)。

cd foo=?            # => foo=3: Not a directory
show_rhs() foo=*    # => foo='3'

我在这里看到的唯一区别是声明和别名是内置的 AND 接受赋值作为参数。根据 -x 选项的输出,似乎在文件名扩展之前添加了一对引号来括起分配。

但是如果文件名扩展在命令执行前运行,不管命令是什么,传入declare 和alias 的参数应该是foo=3 而不是foo=?和 foo=* 由于存在文件 "foo=3".

那么 Bash 是否根据文件名扩展前的命令对 "a=b" 类参数做一些特殊的事情(也许引用通配符?)?

(我的环境:CentOS 5.8 64bit, GNU Bash 3.2.25)

Bash 以一种不严格 Posix 兼容的方式解析它的一些内置命令,并且也没有很好地记录。

特别是接受此类参数的命令中的赋值参数(aliasdeclareexportlocalreadonlytypeset) 不受路径名扩展或分词的影响。 (这是通过抑制扩展在内部完成的,而不是通过引用元字符,尽管很难看出实现细节如何变得可见。)

即使 bash 以 Posix 模式或 sh 模式启动,也会发生这种情况。

请注意,抑制路径名扩展仅适用于看起来像赋值的参数。扩展问题中的示例:

touch foo=3    # Create a file with name "foo=3"
+ touch foo=3
declare foo=?
+ declare 'foo=?'

bar="foo=?"    # Put the declare argument in a variable
+ bar='foo=?'
declare $bar
+ declare foo=3

正如预期的那样,dash 路径名将参数扩展并分词到 aliasexport,与 Posix 规范一致。因此,显然 zsh.

除了在 Posix 模式下,bash 还扩展了看起来像赋值的参数的右侧。在 Posix 模式下,它将此限制为上面列出的内置函数的赋值参数,尽管 Posix 仅在命令字之前的变量赋值中指定 = 之后的波浪号扩展。这就是 dash 所做的,但是 zsh 将其扩展到 "commands of the typeset family"(在 zsh manual 中记录)。