聲明命令和 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 `('

相關內容