$ VAR vs $ {VAR} y para cotizar o no cotizar

Respuestas:

99

VAR=$VAR1es una versión simplificada de VAR=${VAR1}. Hay cosas que el segundo puede hacer que el primero no puede, por ejemplo, hacer referencia a un índice de matriz (no portátil) o eliminar una subcadena (POSIX-portátil). Consulte la sección Más sobre variables de la Guía Bash para principiantes y Expansión de parámetros en la especificación POSIX.

Usar comillas alrededor de una variable como en rm -- "$VAR1"o rm -- "${VAR}"es una buena idea. Esto hace que el contenido de la variable sea una unidad atómica. Si el valor de la variable contiene espacios en blanco (bueno, caracteres en la $IFSvariable especial, espacios en blanco por defecto) o caracteres globales y no lo cita, entonces cada palabra se considera para la generación de nombre de archivo (globalización) cuya expansión genera tantos argumentos para lo que sea que estas haciendo.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

Sobre portabilidad: de acuerdo con POSIX.1-2008 sección 2.6.2 , las llaves son opcionales.

Shawn J. Goff
fuente
@shawn actualizó mi pregunta porque también tengo curiosidad sobre la portabilidad
xenoterracide
@shawn: Dudo que tu ejemplo sea válido. ¿Tiene algún ejemplo real de un shell donde la var1=$varexpansión da un error?
alex
@ Alex: Gracias. Pensé que había probado eso en la línea de comando, pero lo hice mal. Cambié el ejemplo.
Shawn J. Goff
Con el ejemplo actualizado, es mejor tener en cuenta que generalmente debería querer la versión citada, ya que el ejemplo es más bien un caso de esquina.
alex
99
@Shawn: las comillas no son necesarias en una tarea. Son necesarios en la mayoría de los otros usos, incluidos export VAR=$VAR1. En cuanto a las llaves, son opcionales (verifique el cuarto párrafo de la sección que citó; este es el caso en todos los shells pre-POSIX y POSIX).
Gilles
61

${VAR}y $VARson exactamente equivalentes Para una expansión de variable simple, la única razón para usar ${VAR}es cuando el análisis de otra manera tomaría demasiados caracteres en el nombre de la variable, como en ${VAR1}_$VAR2(que sin llaves sería equivalente a ${VAR1_}$VAR2). Expansiones más adornados ( ${VAR:=default}, ${VAR#prefix}, ...) requieren apoyos.

En una asignación variable, la división del campo (es decir, la división en el espacio en blanco en el valor) y la expansión del nombre de ruta (es decir, globbing) se desactivan, por lo que VAR=$VAR1es exactamente equivalente a VAR="$VAR1", en todos los shells POSIX y en todos los sh anteriores a POSIX de los que he oído hablar. . (Ref. POSIX: comandos simples ). Por la misma razón, se VAR=*establece de manera confiable VARen la cadena literal *; por supuesto, VAR=a bestablece VARque aya que bes una palabra separada en primer lugar. En términos generales, las comillas dobles son innecesarias cuando la sintaxis de shell espera una sola palabra, por ejemplo encase … in (pero no en el patrón), pero incluso allí debe tener cuidado: por ejemplo, POSIX especifica quelos objetivos de redirección ( >$filename) no requieren comillas en los scripts, pero algunos shells, incluido bash, requieren comillas dobles incluso en scripts. Ver ¿ Cuándo es necesaria la doble cita? para un análisis más completo

Necesita las comillas dobles en otros casos, en particular en export VAR="${VAR1}"(que se puede escribir de manera equivalente export "VAR=${VAR1}") en muchos shells (POSIX deja este caso abierto). La similitud de este caso con asignaciones simples y la naturaleza dispersa de la lista de casos en los que no necesita comillas dobles, es por eso que recomiendo usar comillas dobles a menos que desee dividir y englobar.

Gilles
fuente
2
Como regla general, siempre citaré expansiones de variables incluso cuando sé que el valor no contendrá ningún IFScarácter porque quiero tener el hábito. La única excepción es que no cito el valor cuando hago una asignación variable (a menos que sea necesario, como cuando el valor contiene un espacio). Esto hace que el resaltado de sintaxis del editor sea más útil cuando hay sustituciones de comandos como FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). En lugar de colorear todo el color de "cadena", obtengo resaltado de sintaxis del código anidado. Por eso también evito los backticks.
Richard Hansen el
Si bien >$fileestá bien en los scripts POSIX, no está en bash incluso cuando no es interactivo (a menos que se cumpla con POSIX $POSIXLY_CORRECTo --posix...).
Stéphane Chazelas
Si bien es cierto que no se necesitan citas VAR=$VAR1, a veces me ha sorprendido local VAR=$VAR1que recuerdo haber trabajado de manera diferente en algunos aspectos, al menos en algunos shells. Pero atm, no puedo reproducir la divergencia.
dubiousjim
Ok, encontré el problema que estaba recordando . Solo aparece en algunas conchas.
dubiousjim
@dubiousjim local VAR=$VAR1es como export VAR=$VAR1, depende del shell.
Gilles
8

Cotización

Tenga en cuenta que la comilla doble se usa para la expansión variable, y la comilla simple se usa para las comillas fuertes, es decir, sin expansión.

Expansión:

this='foo'
that='bar'
these="$this"
those='$that'

Salida:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Puede valer la pena mencionar que debe usar la cita siempre que sea posible por varias razones, entre las cuales se considera la mejor práctica y la legibilidad. También porque Bash es peculiar a veces y a menudo por formas aparentemente ilógicas o irrazonables / inesperadas, y la cita cambia las expectativas implícitas a explícitas, lo que reduce esa superficie de error (o potencial para ello).

Y aunque es completamente legal no cotizar, y funcionará en la mayoría de los casos, esa funcionalidad se proporciona por conveniencia y probablemente sea menos portátil. La práctica totalmente formal garantizada para reflejar la intención y la expectativa es citar.

Sustitución

Ahora considere también que la construcción "${somevar}"se usa para operaciones de sustitución. Varios casos de uso, como reemplazo y matrices.

Reemplazo (pelado):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Reemplazo (reemplazo):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Matrices:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Todo esto apenas está rascando la superficie de la "${var}"construcción de sustitución. La referencia definitiva para el script de shell Bash es la referencia en línea libre, TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

SYANiDE
fuente
1
muy informativo.
Orion Elenzil
0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

termina entonces:

env=$1
    if [ ! -f /dirname/${env}hostname ]

Vale la pena mencionar como un ejemplo más claro del uso de curvas

ninjabber
fuente