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 兼容的方式解析它的一些内置命令,并且也没有很好地记录。
特别是接受此类参数的命令中的赋值参数(alias
、declare
、export
、local
、readonly
和 typeset
) 不受路径名扩展或分词的影响。 (这是通过抑制扩展在内部完成的,而不是通过引用元字符,尽管很难看出实现细节如何变得可见。)
即使 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
路径名将参数扩展并分词到 alias
和 export
,与 Posix 规范一致。因此,显然 zsh
.
除了在 Posix 模式下,bash
还扩展了看起来像赋值的参数的右侧。在 Posix 模式下,它将此限制为上面列出的内置函数的赋值参数,尽管 Posix 仅在命令字之前的变量赋值中指定 =
之后的波浪号扩展。这就是 dash
所做的,但是 zsh
将其扩展到 "commands of the typeset family"(在 zsh manual 中记录)。
我了解到当 运行 在 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 兼容的方式解析它的一些内置命令,并且也没有很好地记录。
特别是接受此类参数的命令中的赋值参数(alias
、declare
、export
、local
、readonly
和 typeset
) 不受路径名扩展或分词的影响。 (这是通过抑制扩展在内部完成的,而不是通过引用元字符,尽管很难看出实现细节如何变得可见。)
即使 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
路径名将参数扩展并分词到 alias
和 export
,与 Posix 规范一致。因此,显然 zsh
.
除了在 Posix 模式下,bash
还扩展了看起来像赋值的参数的右侧。在 Posix 模式下,它将此限制为上面列出的内置函数的赋值参数,尽管 Posix 仅在命令字之前的变量赋值中指定 =
之后的波浪号扩展。这就是 dash
所做的,但是 zsh
将其扩展到 "commands of the typeset family"(在 zsh manual 中记录)。