Tropecei por acidente no seguinte bash
comportamento, 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
- 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
- Divisão de palavras
- 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 bash
não aceite a linha 9?Ou, dito de forma diferente,o que estou perdendo na maneira como a linha 9 é processada, bash
que 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_state
variável.
Um sinalizador mantido nessa variável de estado é PST_ASSIGNOK
. Isso será definido quando algum componente interno que foi analisado como um WORD
token tiver ASSIGNMENT_BUILTIN
em seus sinalizadores.
Esses "integrados de atribuição" são local
, typeset
, declare
, alias
, export
e readonly
.
Isso PST_ASSIGNOK
direcionará o analisador para considerar os parênteses como parte de um WORD
token 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 ( WORD
que 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 `('