Declarar comando y expansión de shell

Declarar comando y expansión de shell

Me topé por accidente con el siguiente bashcomportamiento, que para mí es algo 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 la expansión del caparazón

  1. Expansión de llaves
    • Expansión de tilde
    • Expansión de parámetros y variables.
    • Expansión aritmética
    • Sustitución de procesos
    • Sustitución de mando
  2. división de palabras
  3. Expansión de nombre de archivo

se realiza en la línea de comando después de dividirla en tokens (seguido de la eliminación de las comillas), pero antes de ejecutar el comando final, no hubiera esperado que la línea 9 fallara.

¿Cuál es la razón detrás de esto, que hace que bashno se acepte la línea 9?O, dicho de otra manera,¿Qué me falta en la forma en que se procesa la línea 9 bashque hace que falle pero que la línea 10 tenga éxito?

En cualquier caso, las comillas no siempre funcionarán de manera sencilla y requerirían atención adicional en caso de que los elementos de la matriz sean cadenas con, por ejemplo, espacios.

Respuesta1

tl;dr; Creo que es sólo una peculiaridad de sintaxis y no se debe suponer que hay un gran diseño detrás de esto.

Bash está usando un archivo generado por bison/yaccanalizador, pero al igual que con muchos otros lenguajes (C, Perl, etc.), no es un analizador "limpio", pero también mantiene algunosestadoseparado/paralelo a la gramática en la parser_statevariable.

Una bandera mantenida en esa variable de estado es PST_ASSIGNOK. Esto se establecerá cuando alguna función incorporada que se analizó como un WORDtoken tuviera ASSIGNMENT_BUILTINsus banderas.

Estas "integraciones integradas de asignación" son local, typeset, declare, aliasy .exportreadonly

Esto PST_ASSIGNOKindicará al analizador que considere los paréntesis como parte de un WORDtoken cuando se usen después de una asignación a la derecha de dicha función incorporada. Pero NO cambiará las reglas que determinan si el token actual es realmente unasignación: Dado que ${foo}=(...)no es una tarea aceptable, no se analizará como una sola palabra y los paréntesis provocarán un error de sintaxis como en echo foo(bar).

Después de analizar una línea de comando, seráexpandido, y como parte de las expansiones, cualquier asignación compuesta ( WORDque esté marcada con W_COMPASSIGN) like var=(1 2)se realizará y reemplazará con var, que luego se pasará como argumento a un like incorporado declare. Pero si declare, después de todas las expansiones, obtiene un argumento de la forma var=(...), lo analizará y expandirá nuevamente.

Entonces, varname=foo; declare "$var=(1 2 3)"puede ser similar a declare foo='(1 2 3)'. O a declare foo=(1 2 3), dependiendo de si la variable ya estaba 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")

No creo que sea una buena idea confiar en este caso de esquina:

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

información relacionada