Declarar comando e expansão do shell

Declarar comando e expansão do shell

Tropecei por acidente no seguinte bashcomportamento, o que para mim é meio inesperado.

# 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")

Desde a expansão do shell

  1. Expansão de cinta
    • Expansão til
    • Expansão de parâmetros e variáveis
    • Expansão aritmética
    • Substituição de processo
    • Substituição de comando
  2. Divisão de palavras
  3. Expansão do nome do arquivo

é executado na linha de comando depois de ter sido dividido em tokens (seguido pela remoção das aspas), mas antes da execução do comando final, eu não esperava que a linha 9 falhasse.

Qual é a lógica por trás disso, que faz com que bashnão aceite a linha 9?Ou, dito de forma diferente,o que estou perdendo na maneira como a linha 9 é processada, bashque faz com que ela falhe, mas faz com que a linha 10 seja bem-sucedida?

Em qualquer caso, a cotação nem sempre funcionará diretamente e exigiria atenção extra caso os elementos da matriz fossem strings com, por exemplo, espaços.

Responder1

dr; Acho que é apenas uma peculiaridade de sintaxe e você não deve presumir algum grande projeto por trás disso.

Bash está usando um arquivo gerado por bison/yaccanalisador, mas assim como acontece com muitas outras linguagens (C, Perl, etc), não é um analisador "limpo", mas também mantém algunsestadoseparado/paralelo à gramática na parser_statevariável.

Um sinalizador mantido nessa variável de estado é PST_ASSIGNOK. Isso será definido quando algum componente interno que foi analisado como um WORDtoken tiver ASSIGNMENT_BUILTINem seus sinalizadores.

Esses "integrados de atribuição" são local, typeset, declare, alias, exporte readonly.

Isso PST_ASSIGNOKdirecionará o analisador para considerar os parênteses como parte de um WORDtoken quando usado após uma atribuição à direita de tal componente interno. Mas isso NÃO mudará as regras que determinam se o token atual é realmente umatribuição: como ${foo}=(...)não é uma atribuição aceitável, ela não será analisada como uma única palavra e os parênteses acionarão um erro de sintaxe, assim como em echo foo(bar).

Depois que uma linha de comando for analisada, ela seráexpandido, e como parte das expansões, qualquer atribuição composta ( WORDque foi marcada com W_COMPASSIGN) like var=(1 2)será executada e substituída por var, que será então passada como um argumento para um like integrado declare. Mas se declare, depois de todas as expansões, obtiver um argumento da forma var=(...), ele irá analisá-lo e expandi-lo novamente.

Então, varname=foo; declare "$var=(1 2 3)"pode ser semelhante a declare foo='(1 2 3)'. Ou para declare foo=(1 2 3), dependendo se a variável já foi definida:

$ 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")

Não acho que seja uma boa ideia confiar neste caso:

$ 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 `('

informação relacionada