El viejo consejo solía ser poner entre comillas dobles cualquier expresión que involucre un $VARIABLE
, al menos si uno quisiera que el shell lo interpretara como un solo elemento; de lo contrario, cualquier espacio en el contenido de $VARIABLE
arrojaría el caparazón.
Entiendo, sin embargo, que en versiones más recientes de shells, las comillas dobles ya no siempre son necesarias (al menos para el propósito descrito anteriormente). Por ejemplo, en bash
:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
En zsh
, por otro lado, los mismos tres comandos tienen éxito. Por lo tanto, según este experimento, parece que, en bash
, se pueden omitir las comillas dobles dentro de [[ ... ]]
, pero no dentro de [ ... ]
ni en argumentos de línea de comandos, mientras que en zsh
, las comillas dobles pueden omitirse en todos estos casos.
Pero inferir reglas generales a partir de ejemplos anecdóticos como el anterior es una propuesta arriesgada. Sería bueno ver un resumen de cuándo son necesarias las comillas dobles. Estoy principalmente interesado en zsh
, bash
y /bin/sh
.
Respuesta aceptada:
Primero, separe zsh del resto. No es una cuestión de shells antiguos o modernos:zsh se comporta de manera diferente. Los diseñadores de zsh decidieron hacerlo incompatible con los shells tradicionales (Bourne, ksh, bash), pero más fácil de usar.
En segundo lugar, es mucho más fácil usar comillas dobles todo el tiempo que recordar cuándo se necesitan. Se necesitan la mayor parte del tiempo, por lo que deberá aprender cuándo no se necesitan, no cuando se necesitan.
En pocas palabras, las comillas dobles son necesarias siempre que se espera una lista de palabras o un patrón . Son opcionales en contextos donde el analizador espera una cadena sin formato.
Qué sucede sin comillas
Tenga en cuenta que sin comillas dobles, suceden dos cosas.
- Primero, el resultado de la expansión (el valor de la variable para una sustitución de parámetro como
${foo}
, o la salida del comando para una sustitución de comando como$(foo)
) se divide en palabras dondequiera que contenga espacios en blanco.
Más precisamente, el resultado de la expansión se divide en cada carácter que aparece en el valor delIFS
variable (carácter separador). Si una secuencia de caracteres separadores contiene espacios en blanco (espacio, tabulador o nueva línea), el espacio en blanco cuenta como un solo carácter; los separadores que no son espacios en blanco al principio, al final o repetidos conducen a campos vacíos. Por ejemplo, conIFS=" :"
,:one::two : three: :four
produce campos vacíos antes deone
, entreone
ytwo
, y (uno solo) entrethree
yfour
. - Cada campo que resulta de la división se interpreta como un globo (un patrón comodín) si contiene uno de los caracteres
[*?
. Si ese patrón coincide con uno o más nombres de archivos, el patrón se reemplaza por la lista de nombres de archivos coincidentes.
Una expansión variable sin comillas $foo
se conoce coloquialmente como el "operador split+glob", en contraste con "$foo"
que simplemente toma el valor de la variable foo
. Lo mismo ocurre con la sustitución de comandos:"$(foo)"
es una sustitución de comando, $(foo)
es una sustitución de comando seguida de split+glob.
Donde puede omitir las comillas dobles
Estos son todos los casos en los que puedo pensar en un shell de estilo Bourne donde puede escribir una variable o sustitución de comando sin comillas dobles, y el valor se interpreta literalmente.
-
En el lado derecho de una tarea.
var=$stuff a_single_star=*
Tenga en cuenta que necesita las comillas dobles después de
export
, porque es un componente ordinario, no una palabra clave. Esto solo es cierto en algunos shells como dash, zsh (en emulación sh), yash o posh; bash y ksh tratan ambosexport
especialmente.export VAR="$stuff"
-
En un
case
declaración.case $var in …
Tenga en cuenta que necesita comillas dobles en un patrón de mayúsculas y minúsculas. La división de palabras no ocurre en un patrón de mayúsculas y minúsculas, pero una variable sin comillas se interpreta como un patrón, mientras que una variable entre comillas se interpreta como una cadena literal.
a_star='a*' case $var in "$a_star") echo "'$var' is the two characters a, *";; $a_star) echo "'$var' begins with a";; esac
-
Dentro de corchetes dobles. Los corchetes dobles son sintaxis especial de shell.
[[ -e $filename ]]
Excepto que necesita comillas dobles donde se espera un patrón o una expresión regular:en el lado derecho de
=
o==
o!=
o=~
.a_star='a*' if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *" elif [[ $var == $a_star ]]; then echo "'$var' begins with a" fi
Necesita comillas dobles como de costumbre entre corchetes simples
[ … ]
porque son sintaxis de shell ordinarias (es un comando que se llama[
). Ver Corchetes simples o dobles -
En una redirección en shells POSIX no interactivos (no
bash
, niksh88
).echo "hello world" >$filename
Algunos shells, cuando son interactivos, tratan el valor de la variable como un patrón comodín. POSIX prohíbe ese comportamiento en shells no interactivos, pero algunos shells incluyen bash (excepto en modo POSIX) y ksh88 (incluso cuando se encuentran como (supuestamente) POSIX
sh
de algunos Unices comerciales como Solaris) todavía lo hacen allí (bash
también intenta dividir y la redirección falla a menos que split+globbing da como resultado exactamente una palabra), por lo que es mejor citar los objetivos de las redirecciones en unsh
script en caso de que quieras convertirlo en unbash
script algún día, o ejecútelo en un sistema dondesh
no cumple con ese punto, o puede ser fuente de conchas interactivas. -
Dentro de una expresión aritmética. De hecho, debe omitir las comillas para que una variable se analice como una expresión aritmética.
expr=2*2 echo "$(($expr))"
Sin embargo, necesita las comillas alrededor de la expansión aritmética, ya que están sujetas a la división de palabras en la mayoría de los shells, como lo requiere POSIX (!?).
-
En un subíndice de matriz asociativa.
typeset -A a i='foo bar*qux' a[foo bar*qux]=hello echo "${a[$i]}"
Una variable sin comillas y una sustitución de comando pueden ser útiles en algunas circunstancias excepcionales:
- Cuando el valor de la variable o la salida del comando consta de una lista de patrones globales y desea expandir estos patrones a la lista de archivos coincidentes.
- Cuando sabe que el valor no contiene ningún carácter comodín,
$IFS
no se modificó y desea dividirlo en caracteres de espacio en blanco. - Cuando desee dividir un valor en un determinado carácter:deshabilite el globbing con
set -f
, establecerIFS
al carácter separador (o déjelo solo para dividir en espacios en blanco), luego haga la expansión.
Zsh
En zsh, puede omitir las comillas dobles la mayoría de las veces, con algunas excepciones.
-
$var
nunca se expande a varias palabras, sin embargo, se expande a la lista vacía (a diferencia de una lista que contiene una sola palabra vacía) si el valor devar
es la cadena vacía. Contraste:var= print -l $var foo # prints just foo print -l "$var" foo # prints an empty line, then foo
Del mismo modo,
"${array[@]}"
se expande a todos los elementos de la matriz, mientras que$array
solo se expande a los elementos no vacíos. -
El
@
la bandera de expansión del parámetro a veces requiere comillas dobles alrededor de toda la sustitución:"${(@)foo}"
. -
La sustitución de comandos se divide en campo si no está entre comillas:
echo $(echo 'a'; echo '*')
imprimea *
(con un solo espacio) mientras queecho "$(echo 'a'; echo '*')"
imprime la cadena de dos líneas sin modificar. Usa"$(somecommand)"
para obtener el resultado del comando en una sola palabra, sin saltos de línea finales. Usa"${$(somecommand; echo _)%?}"
para obtener el resultado exacto del comando, incluidas las nuevas líneas finales. Utilice"${(@f)$(somecommand)}"
para obtener una matriz de líneas de la salida del comando.