¿Cuándo necesitamos llaves entre las variables de shell?

659

En los scripts de shell, ¿cuándo usamos {}al expandir variables?

Por ejemplo, he visto lo siguiente:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

¿Hay una diferencia significativa, o es solo estilo? ¿Se prefiere uno sobre el otro?

Nuevo Usuario
fuente

Respuestas:

754

En este ejemplo particular, no hay diferencia. Sin embargo, los {}in ${}son útiles si desea expandir la variable fooen la cadena

"${foo}bar"

ya "$foobar"que en cambio expandiría la variable identificada por foobar.

Las llaves también se requieren incondicionalmente cuando:

  • elementos de matriz desplegables, como en ${array[42]}
  • usando operaciones de expansión de parámetros, como en ${filename%.*}(eliminar extensión)
  • expandiendo los parámetros posicionales más allá de 9: "$8 $9 ${10} ${11}"

Hacer esto en todas partes, en lugar de solo en casos potencialmente ambiguos, puede considerarse una buena práctica de programación. Esto es tanto por coherencia como para evitar sorpresas como $foo_$bar.jpg, donde no es visualmente obvio que el guión bajo se convierta en parte del nombre de la variable.

Fred Foo
fuente
106
{}se conoce como expansión de llaves . ${}se conoce como expansión variable. Ellos hacen cosas diferentes. Te votaré a excepción del bit de no expansión.
Spencer Rathbun
55
@NewUser " Por lo tanto, aparte de las matrices, no es realmente necesario " No es así, los corchetes son necesarios para PARAMETER EXPANSION , una construcción muy útil en la creación de secuencias de comandos. He visto muchos scripts sed y awk que pueden reemplazarse con un poco de expansión de parámetros.
SiegeX
10
@caffinatedmonkey $()se usa para ejecutar un comando, de modo que md5sum=$(md5sum foo.bin)almacenará la salida de md5sum foo.binen la variable md5sum, ahora accesible usando ${md5sum}. Además, ¡+1 y muchos más en espíritu a OP por mencionar que es una buena práctica ser explícito!
L0j1k
11
@ L0j1k Hablando de lo explícito, me parece importante mencionar que $()ejecuta su comando desde una subshell .
Adrian Günter
2
@karatedog ${1:-20}es una forma de expansión de parámetros. Aquí no es obvio porque usa principalmente dígitos y operadores aritméticos que nos engañan al pensar que hay aritmética involucrada, pero en realidad se refiere al parámetro posicional $1, que si no se define será reemplazado por un valor predeterminado 20(la sintaxis es ${variable:-default_value}).
Aaron
127

Las variables se declaran y asignan sin $y sin {}. Tienes que usar

var=10

asignar. Para leer desde la variable (en otras palabras, 'expandir' la variable), debe usar $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Esto me ha confundido a veces: en otros idiomas, nos referimos a la variable de la misma manera, independientemente de si está a la izquierda o a la derecha de una tarea. Pero el scripting de shell es diferente, ¡ $var=10no hace lo que crees que hace!

Aaron McDaid
fuente
34

Usas {}para agrupar. Las llaves son necesarias para desreferenciar los elementos de la matriz. Ejemplo:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
Glenn Jackman
fuente
No pude entender la primera línea dir=(*). Hasta donde sé, dires un comando incorporado para enumerar el contenido del directorio (equivalente a ls -C -b). ¿Podría explicar por favor?
Jarvis
1
En la programación de shell, los comandos y argumentos deben estar separados entre sí por espacios en blanco. Aquí, ve el signo igual sin espacios en blanco, lo que significa que es una asignación variable. dires el nombre de la variable, y los paréntesis se utilizan para recopilar la expansión del nombre de archivo *en una matriz.
Glenn Jackman
27

También puede realizar algunas manipulaciones de texto dentro de las llaves:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Resultado:

./folder/subfolder/file.txt ./folder

o

STRING="This is a string"
echo ${STRING// /_}

Resultado:

This_is_a_string

Tienes razón en "variables regulares" no son necesarias ... Pero es más útil para la depuración y para leer un script.

SierraX
fuente
11

El final del nombre de la variable generalmente se indica con un espacio o una nueva línea. Pero, ¿qué pasa si no queremos un espacio o una nueva línea después de imprimir el valor variable? Las llaves indican al intérprete de shell dónde está el final del nombre de la variable.

Ejemplo clásico 1): variable de shell sin espacios en blanco al final

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Ejemplo 2) Classpath de Java con jarras versionadas

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(La respuesta de Fred ya lo dice, pero su ejemplo es demasiado abstracto)

Sridhar Sarnobat
fuente
5

Las llaves se necesitan siempre para acceder a los elementos de la matriz y llevar a cabo la expansión de llaves.

Es bueno no ser demasiado cauteloso y usarlo {}para la expansión variable de shell incluso cuando no hay margen para la ambigüedad.

Por ejemplo:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Por lo tanto, es mejor escribir las tres líneas como:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

que definitivamente es más legible.

Como un nombre de variable no puede comenzar con un dígito, el shell no necesita {}alrededor de variables numeradas (como $1, $2etc.) a menos que dicha expansión sea seguida por un dígito. Eso es demasiado sutil y hace que se use explícitamente {}en tales contextos:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Ver:

codeforester
fuente
1
It's good to be not over-cautious: Me pregunto qué piensa la mayoría de la gente. Use llaves todo el tiempo para no olvidarlas cuando las necesite, o úselas solo donde sea necesario, para mejorar la legibilidad.
Roger Dahl
1
Creo que es la falta de conciencia lo que lleva a los programadores a usar curvas incluso cuando no son necesarias. Esta ignorancia es similar al otro error común de no usar comillas dobles para evitar la división o el bloqueo involuntario de palabras. En la base de esto, la realidad es que los programadores no toman en serio las secuencias de comandos de shell tanto como otros lenguajes de secuencias de comandos como Python y Ruby.
codeforester
1
Eso es cierto. Mi motivo favorito es que todo el mundo parece pensar que todas las variables deberían ser mayúsculas en los scripts de shell :)
Roger Dahl
2

Siguiendo las sugerencias de SierraX y Peter sobre la manipulación de texto, las llaves {}se utilizan para pasar una variable a un comando, por ejemplo:

Digamos que tiene un archivo sposi.txt que contiene la primera línea de una conocida novela italiana:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ouput: quel ramo del lago di como che volge a mezzogiorno

Ahora cree dos variables:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Ahora sustituya el contenido variable de la palabra con el de new_word , dentro del archivo sposi.txt

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ouput: quel filone del lago di como che volge a mezzogiorno

La palabra "ramo" ha sido reemplazada.

Fabio Natalini
fuente
1
Esto funciona igual de bien sin llaves alrededor de las variables.
Armali
Es posible que desee arreglar el weel-known novelbit. Votado sin embargo.
GSL