声明命令和 shell 扩展

声明命令和 shell 扩展

我偶然发现了以下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")

由于外壳扩展

  1. 支撑扩张
    • 波形符扩展
    • 参数和变量扩展
    • 算术扩展
    • 工艺替代
    • 命令替换
  2. 分词
  3. 文件名扩展

在它被分割成标记(然后删除引号)之后但在执行最终命令之前在命令行上执行,我不会预期第 9 行会失败。

其背后的基本原理是什么,导致bash不接受第 9 行?或者,换句话说,我在处理第 9 行的方式中缺少什么,bash使其失败但使第 10 行成功?

无论如何,引用并不总是直接有效,并且如果数组元素是带有空格等字符串,则需要额外注意。

答案1

太长;博士;我认为这只是一个语法怪癖,你不应该假设它背后有一些宏伟的设计。

Bash 使用 bison/yacc 生成的解析器,但就像许多其他语言(C、perl 等)一样,它不是一个“干净”的解析器,但它也保留了一些状态与变量中的语法分离/并行parser_state

该状态变量中保存的标志是PST_ASSIGNOK。当某些被解析为WORD令牌的内置函数ASSIGNMENT_BUILTIN在其标志中时,将会设置该值。

这样的“赋值内置函数”是localtypesetdeclarealias和。exportreadonly

当在此类内置函数右侧赋值之后使用时,将指示解析器将括号视为标记的PST_ASSIGNOK一部分。WORD但它不会改变确定当前令牌是否实际上是一个的规则任务: 由于${foo}=(...)不是可接受的赋值,因此它不会被解析为单个单词,并且括号将触发语法错误,就像 中一样echo foo(bar)

解析命令行后,它将是扩大,并且作为扩展的一部分,任何复合赋值(WORD用 标记W_COMPASSIGNvar=(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 `('

相关内容