我偶然发现了以下bash
行为,这对我来说有点意外。
# The following works
$ declare bar=Hello # Line 1
$ declare -p bar # Line 2
declare -- bar="Hello"
$ foo=bar # Line 3
$ declare ${foo}=Bye # Line 4
$ declare -p bar # Line 5
declare -- bar="Bye"
# The following fails, though
$ declare -a array=( A B C ) # Line 6
$ declare -p array # Line 7
declare -a array=([0]="A" [1]="B" [2]="C")
$ foo=array # Line 8
$ declare -a ${foo}=([0]="A" [1]="XXX" [2]="C") # Line 9
bash: syntax error near unexpected token `('`
# Quoting the assignment fixes the problem
$ declare -a "${foo}=(A YYY C)" # Line 10
$ declare -p array # Line 11
declare -a array=([0]="A" [1]="YYY" [2]="C")
由于外壳扩展
- 支撑扩张
- 波形符扩展
- 参数和变量扩展
- 算术扩展
- 工艺替代
- 命令替换
- 分词
- 文件名扩展
在它被分割成标记(然后删除引号)之后但在执行最终命令之前在命令行上执行,我不会预期第 9 行会失败。
其背后的基本原理是什么,导致bash
不接受第 9 行?或者,换句话说,我在处理第 9 行的方式中缺少什么,bash
使其失败但使第 10 行成功?
无论如何,引用并不总是直接有效,并且如果数组元素是带有空格等字符串,则需要额外注意。
答案1
太长;博士;我认为这只是一个语法怪癖,你不应该假设它背后有一些宏伟的设计。
Bash 使用 bison/yacc 生成的解析器,但就像许多其他语言(C、perl 等)一样,它不是一个“干净”的解析器,但它也保留了一些状态与变量中的语法分离/并行parser_state
。
该状态变量中保存的标志是PST_ASSIGNOK
。当某些被解析为WORD
令牌的内置函数ASSIGNMENT_BUILTIN
在其标志中时,将会设置该值。
这样的“赋值内置函数”是local
、typeset
、declare
、alias
和。export
readonly
当在此类内置函数右侧赋值之后使用时,将指示解析器将括号视为标记的PST_ASSIGNOK
一部分。WORD
但它不会改变确定当前令牌是否实际上是一个的规则任务: 由于${foo}=(...)
不是可接受的赋值,因此它不会被解析为单个单词,并且括号将触发语法错误,就像 中一样echo foo(bar)
。
解析命令行后,它将是扩大,并且作为扩展的一部分,任何复合赋值(WORD
用 标记W_COMPASSIGN
)var=(1 2)
将被执行并替换为var
,然后将其作为参数传递给内置函数如declare
。但是declare
,如果在所有扩展之后获得 形式的参数var=(...)
,它将再次解析并扩展它本身。
所以,varname=foo; declare "$var=(1 2 3)"
可能类似于declare foo='(1 2 3)'
.或者到declare foo=(1 2 3)
,取决于变量是否已经定义:
$ declare 'foo=(1 2 3)'; typeset -p foo
declare -- foo="(1 2 3)"
$ declare foo=(1); typeset -p foo
declare -a foo=([0]="1")
$ declare 'foo=(1 2 3)'; typeset -p foo
declare -a foo=([0]="1" [1]="2" [2]="3")
我认为依赖这个极端情况不是一个好主意:
$ declare 'bar=(1 ( )'; typeset -p bar
declare -- bar="(1 ( )"
$ declare bar=(1); typeset -p bar
declare -a bar=([0]="1")
$ declare 'bar=(1 ( )'; typeset -p bar
bash: syntax error near unexpected token `('