我偶然發現了以下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 `('