¿Cuándo colocar comillas alrededor de una variable de shell?

Resuelto Cristian asked hace 12 años • 4 respuestas

¿Debería o no incluir entre comillas las variables en un script de Shell?

Por ejemplo, ¿es correcto lo siguiente?

xdg-open $URL
[ $? -eq 2 ]

o

xdg-open "$URL"
[ "$?" -eq "2" ]

Y si es así, ¿por qué?

Cristian avatar Apr 09 '12 06:04 Cristian
Aceptado

Regla general: cítelo si puede estar vacío o contener espacios (o cualquier espacio en blanco en realidad) o caracteres especiales (comodines). No citar cadenas con espacios a menudo lleva a que el shell rompa un único argumento en muchos.

$?no necesita comillas ya que es un valor numérico. Si $URLes necesario depende de lo que permita allí y de si aún desea un argumento si está vacío.

Tiendo a citar siempre cadenas simplemente por costumbre, ya que así es más seguro.

paxdiablo avatar Apr 08 '2012 23:04 paxdiablo

En resumen, cite todo lo que no requiera que el shell realice la división de palabras y la expansión de comodines.

Las comillas simples protegen el texto entre ellas palabra por palabra. Es la herramienta adecuada cuando necesitas asegurarte de que el caparazón no toque la cuerda en absoluto. Normalmente, es el mecanismo de cotización preferido cuando no se requiere interpolación variable.

$ echo 'Nothing \t in here $will change'
Nothing \t in here $will change

$ grep -F '@&$*!!' file /dev/null
file:I can't get this @&$*!! quoting right.

Las comillas dobles son adecuadas cuando se requiere interpolación variable. Con las adaptaciones adecuadas, también es una buena solución cuando se necesitan comillas simples en la cadena. (No existe una forma sencilla de escapar de una comilla simple entre comillas simples, porque no existe un mecanismo de escape dentro de las comillas simples; si lo hubiera, no citarían completamente palabra por palabra).

$ echo "There is no place like '$HOME'"
There is no place like '/home/me'

No son adecuadas las comillas cuando se requiere específicamente que el shell realice una división de palabras y/o una expansión de comodines.

División de palabras (también conocida como división de tokens);

 $ words="foo bar baz"
 $ for word in $words; do
 >   echo "$word"
 > done
 foo
 bar
 baz

Por el contrario:

 $ for word in "$words"; do echo "$word"; done
 foo bar baz

(El bucle solo se ejecuta una vez, sobre la única cadena entre comillas).

 $ for word in '$words'; do echo "$word"; done
 $words

(El bucle solo se ejecuta una vez, sobre la cadena literal entre comillas simples).

Expansión comodín:

$ pattern='file*.txt'
$ ls $pattern
file1.txt      file_other.txt

Por el contrario:

$ ls "$pattern"
ls: cannot access file*.txt: No such file or directory

(No hay ningún archivo cuyo nombre sea literal file*.txt).

$ ls '$pattern'
ls: cannot access $pattern: No such file or directory

$pattern(¡ Tampoco hay ningún archivo llamado !)

En términos más concretos, cualquier cosa que contenga un nombre de archivo normalmente debe ir entre comillas (porque los nombres de archivo pueden contener espacios en blanco y otros metacaracteres del shell). Todo lo que contenga una URL normalmente debe ir entre comillas (porque muchas URL contienen metacaracteres de shell como ?y &). Cualquier cosa que contenga una expresión regular generalmente debe citarse (lo mismo ocurre). Todo lo que contenga espacios en blanco significativos que no sean espacios simples entre caracteres que no sean espacios en blanco debe estar entre comillas (porque de lo contrario, el shell dividirá los espacios en blanco, efectivamente, en espacios simples y recortará cualquier espacio en blanco inicial o final).

Cuando sabes que una variable sólo puede contener un valor que no contiene metacaracteres de shell, las comillas son opcionales. Por lo tanto, un valor sin comillas $?está básicamente bien, porque esta variable solo puede contener un único número. Sin embargo, "$?"también es correcto y se recomienda por motivos de coherencia y corrección generales (aunque esta es mi recomendación personal, no una política ampliamente reconocida).

Los valores que no son variables siguen básicamente las mismas reglas, aunque también se pueden escapar los metacaracteres en lugar de citarlos. Para un ejemplo común, una URL con a &en será analizada por el shell como un comando en segundo plano a menos que el metacarácter esté entre comillas o con carácter de escape:

$ wget http://example.com/q&uack
[1] wget http://example.com/q
-bash: uack: command not found

(Por supuesto, esto también sucede si la URL está en una variable sin comillas). Para una cadena estática, las comillas simples tienen más sentido, aunque aquí funciona cualquier forma de comillas o escape.

wget 'http://example.com/q&uack'  # Single quotes preferred for a static string
wget "http://example.com/q&uack"  # Double quotes work here, too (no $ or ` in the value)
wget http://example.com/q\&uack   # Backslash escape
wget http://example.com/q'&'uack  # Only the metacharacter really needs quoting

El último ejemplo también sugiere otro concepto útil, que me gusta llamar "citas oscilantes". Si necesita mezclar comillas simples y dobles, puede usarlas una al lado de la otra. Por ejemplo, las siguientes cadenas entre comillas

'$HOME '
"isn't"
' where `<3'
"' is."

se pueden pegar uno detrás del otro, formando una única cadena larga después de la tokenización y la eliminación de las comillas.

echo '$HOME '"isn't"' where `<3'"' is."
$HOME isn't where `<3' is.

Esto no es muy legible, pero es una técnica común y, por lo tanto, es bueno saberla.

Además, los scripts normalmente no deberían usarse lspara nada. Para expandir un comodín, simplemente... úselo.

$ printf '%s\n' $pattern   # not ``ls -1 $pattern''
file1.txt
file_other.txt

$ for file in $pattern; do  # definitely, definitely not ``for file in $(ls $pattern)''
>  printf 'Found file: %s\n' "$file"
> done
Found file: file1.txt
Found file: file_other.txt

(El bucle es completamente superfluo en el último ejemplo; printfespecíficamente también funciona bien con múltiples argumentos stat. Pero hacer un bucle sobre una coincidencia comodín es un problema común y con frecuencia se realiza de forma incorrecta).

Una variable que contiene una lista de tokens para recorrer o un comodín para expandir se ve con menos frecuencia, por lo que a veces abreviamos "citar todo a menos que sepas exactamente lo que estás haciendo".

tripleee avatar Dec 30 '2014 07:12 tripleee