¿Cuándo se expande {a, b, c} en bash, cuándo no?

13

Un script bash que contiene

for i in {a,b}-{1,2}; do
  echo $i;
done

huellas dactilares

a-1
a-2
b-1
b-2

cuando se ejecuta Esto es lo que esperaba, ya que la {a,b}construcción se expande.

Sin embargo, cuando (otro) script contiene

v={a,b}-{1,2}
echo $v

se imprime

{a,b}-{1,2}

que no es lo que esperaba Esperaba que se imprimiera a-1 a-2 b-1 b-2. Obviamente, la {a,b}construcción no se expande.

Puedo hacer que se expanda así

v=$(echo {a,b}-{1,2})

En base a estas observaciones tengo dos preguntas: 1) ¿cuándo se {a,b}expande el constructo? 2) ¿es $(echo {a,b}-{1,2})la forma preferida de desencadenar una expansión cuando sea necesario?

René Nyffenegger
fuente
1
Esto es, al menos, coherente con el hecho de que no puede hacer una variable de matriz con un simple =. Por ejemplo, v=a-1 a-2no funcionará.
grochmal
@grochmal: eso es porque estás asignando un valor escalar. v=a-1 a-2significa assign 'a-1' to variable v and run 'a-2' v=(a-1 a-2)asigna la matriz a la variable v. v+=(b-1 b-2)se le suma.
cas

Respuestas:

15

El manual de Bash dice que:

SIMPLE COMMAND EXPANSION
When a simple command is executed, the shell performs the following
expansions, assignments, and redirections, from left to right.
[...]
4. The  text  after the = in each variable assignment undergoes tilde
   expansion, parameter expansion, command substitution, arithmetic
   expansion, and quote removal before being assigned to the variable.

La expansión de llaves no está en la lista, por lo que no se realiza para la tarea v={a,b}-{1,2}. Como mencionó @Wildcard, la simple expansión a no v=a-1 v=b-1 ...tendría sentido de todos modos.

Además, al ejecutar el echo $v, se aplica lo siguiente:

EXPANSION
Expansion is performed on the command line after it has been split
into words. [...]

The order of expansions is: brace expansion; tilde expansion, 
parameter and variable expansion, arithmetic expansion, and command
substitution (done in a left-to-right fashion); word splitting; and
pathname expansion.

La expansión de llaves ocurre antes de la expansión variable, por lo que las llaves asignadas a $vno se expanden.

Pero puedes hacer cosas como esta:

$ var_this=foo var_that=bar
$ echo $var_{this,that}
foo bar

Expandirlo $(echo ...)debería funcionar si no tiene ningún espacio en blanco en la cadena para expandir, y por lo tanto no tendrá problemas con la división de palabras. Una mejor manera podría ser usar una variable de matriz si puede.

Por ejemplo, guarde la expansión en una matriz y ejecute algún comando con los valores expandidos:

$ v=( {a,b}-{1,2} )
$ some_command "${v[@]}"
ilkkachu
fuente
5

Un punto interesante Posiblemente útil es el siguiente extracto de man bash:

Una variable puede ser asignada por una declaración de la forma

      nombre = [ valor ]

Si no se proporciona el valor , a la variable se le asigna la cadena nula. Todos los valores se someten a expansión de tilde, expansión de parámetros y variables, sustitución de comandos, expansión aritmética y eliminación de comillas (ver EXPANSIÓN a continuación).

Tenga en cuenta que la expansión de llaves NO se menciona en la lista.

Sin embargo, esto todavía deja una pregunta, a saber: ¿Cómo sabe el shell que se trata de una asignación variable y, por lo tanto, no está sujeta a la expansión de llaves? O más precisamente, ¿dónde se clarifica y codifica la secuencia de análisis de manera que el shell se defina para identificar asignaciones variables antes de que maneje la expansión de llaves? (Obviamente, así es como bashfunciona, pero no he encontrado la línea exacta de documentación que describe esto).

Comodín
fuente
1
Tal vez esto ? "2. Las palabras que no son asignaciones variables o redirecciones se expanden (ver Expansiones de Shell)".
Jeff Schaller
@JeffSchaller, tienes razón. En realidad, esta pregunta se responde mejor con solo leer la sección "EXPANSIÓN DE COMANDO SIMPLE" ( LESS=+/SIMPLE man bash).
Comodín el
0

según mi pequeño conocimiento, {a, b, c} se expande cuando se repite directamente o se usa con un comando, por ejemplo: mkdir ~ / {a, b, c}, pero cuando se establece en una variable, debe evaluarse antes haciéndolo eco o usándolo como argumento!

u@h:~$ echo {a,b}-{1,2}
a-1 a-2 b-1 b-2
u@h:~$ v={a,b}-{1,2}
u@h:~$ echo $v
{a,b}-{1,2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2

como tiene "a" seguido de "b" en orden alfabético [az] y "1" seguido de "2" en orden dec [0-9]: puede usar el punto doble ".." en lugar de coma ", "

u@h:~$ echo {a..b}-{1..2}
a-1 a-2 b-1 b-2
u@h:~$ v={a..b}-{1..2}
u@h:~$ echo $v
{a..b}-{1..2}
u@h:~$ eval echo $v
a-1 a-2 b-1 b-2
Jonás
fuente
1
La pregunta es por qué vno se configuró en la cadena literal "a-1 a-2 b-1 b-2". O, al menos, por qué no se produjo ningún error al escribir el comando: v=a-1 v=a-2 v=b-1 v=b-2(que esperaría que se ampliara esa asignación de variable). Es una buena pregunta; Esto realmente no responde.
Comodín el
@SatoKatsura, ¿qué se supone que significa "expansión de comodines"? Hay expansión de parámetros, expansión de nombre de ruta y expansión de llaves, cualquiera de las cuales podría estar refiriéndose, y todas las cuales son diferentes. ¿O te refieres a la división de palabras?
Comodín el
0

Asignar a una variable en bash no expande la expresión.

Para este pequeño script, x contendrá "*"y no la expansión de "*":

#!/bin/bash
x=*
echo "$x"

Sin embargo, algunos valores se expanden, ref. la buena respuesta de ilkkachu.

Las expresiones se expanden cuando se evalúan.

Me gusta esto:

x=$(echo *)        # <-- evaluation of "*"
echo "$x"

O así:

x=*
echo $x            # <-- evaluation of $x

O así:

x=*
eval echo "$x"     # <-- evaluation of `echo *`

La activación $()es bastante común y creo que es preferible eval, pero lo mejor es probablemente no activar la evaluación en absoluto, hasta que la variable se use realmente en un comando.

Alejandro
fuente