私は偶然、次のような動作に遭遇しました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
tl;dr; これは単なる構文上の癖であり、その背後に何らかの壮大な計画があると想定すべきではないと思います。
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 `('