¿Podría alguien decirme si debo ajustar o no las comillas alrededor de las variables en un script de shell?
Por ejemplo, es lo siguiente correcto:
xdg-open $URL
[ $? -eq 2 ]
o
xdg-open "$URL"
[ "$?" -eq "2" ]
Y si es así, ¿por qué?
¿Podría alguien decirme si debo ajustar o no las comillas alrededor de las variables en un script de shell?
Por ejemplo, es lo siguiente correcto:
xdg-open $URL
[ $? -eq 2 ]
o
xdg-open "$URL"
[ "$?" -eq "2" ]
Y si es así, ¿por qué?
Respuestas:
Regla general: cítela si puede estar vacía o contener espacios (o cualquier espacio en blanco realmente) o caracteres especiales (comodines). No citar cadenas con espacios a menudo conduce a que el shell rompa un solo argumento en muchos.
$?
no necesita comillas ya que es un valor numérico. Si lo $URL
necesita depende de lo que permita allí y de si aún quiere un argumento si está vacío.
Siempre tiendo a citar cadenas solo por costumbre, ya que es más seguro de esa manera.
IFS=0
, entonces echo $?
puede ser muy sorprendente.
cp $source1 $source2 $dest
, pero si por alguna razón inesperada dest
no se establece, el tercer argumento simplemente desaparece y se copiará source1
en silencio en source2
lugar de darle un error apropiado para el destino en blanco (como lo hubiera hecho si hubiera citado cada argumento).
quote it if...
tiene el proceso de pensamiento al revés: las citas no son algo que agrega cuando lo necesita, son algo que elimina cuando lo necesita. Siempre ajuste las cadenas y los scripts en comillas simples, a menos que necesite usar comillas dobles (por ejemplo, para permitir que una variable se expanda) o no necesite comillas (por ejemplo, para hacer globings y expandir nombres de archivos).
En resumen, cita todo lo que no requieras que el shell realice la división de tokens y la expansión de comodines.
Las comillas simples protegen el texto entre ellas literalmente. Es la herramienta adecuada cuando necesita asegurarse de que el shell no toque la cadena en absoluto. Por lo general, es el mecanismo de cotización elegido 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 adaptaciones adecuadas, también es una buena solución cuando necesita comillas simples en la cadena. (No hay una forma directa de escapar de una cita simple entre comillas simples, porque no hay un mecanismo de escape dentro de las comillas simples; si existiera, no citarían completamente al pie de la letra).
$ echo "There is no place like '$HOME'"
There is no place like '/home/me'
No hay comillas adecuadas cuando se requiere específicamente que el shell realice una división de tokens y / o expansión de comodines.
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 cadena única entre comillas).
$ for word in '$words'; do echo "$word"; done
$words
(El ciclo 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 llamado literalmente file*.txt
).
$ ls '$pattern'
ls: cannot access $pattern: No such file or directory
(¡No hay ningún archivo llamado $pattern
tampoco!)
En términos más concretos, cualquier cosa que contenga un nombre de archivo debe ser citado (porque los nombres de archivo pueden contener espacios en blanco y otros metacaracteres de shell). Cualquier cosa que contenga una URL generalmente debe citarse (porque muchas URL contienen metacaracteres de shell como ?
y &
). Cualquier cosa que contenga una expresión regular generalmente debe citarse (ídem ídem). Cualquier cosa que contenga espacios en blanco significativos que no sean espacios individuales entre caracteres que no sean espacios en blanco debe ser citado (porque de lo contrario, el shell combinará el espacio en blanco, efectivamente, en espacios individuales y recortará cualquier espacio en blanco inicial o final).
Cuando sabe que una variable solo puede contener un valor que no contiene metacaracteres de shell, las comillas son opcionales. Por lo tanto, un sin comillas $?
está básicamente bien, porque esta variable solo puede contener un solo número. Sin embargo, "$?"
también es correcto y se recomienda por su consistencia y corrección general (aunque esta es mi recomendación personal, no es una política ampliamente reconocida).
Los valores que no son variables básicamente siguen las mismas reglas, aunque también podría escapar a los metacaracteres en lugar de citarlos. Para un ejemplo común, &
el shell analizará una URL con una entrada como un comando de fondo a menos que se escape o se cite el metacarácter:
$ 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 cualquier forma de cita o escape funciona aquí.
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 "cotización de balancín". Si necesita mezclar comillas simples y dobles, puede usarlas adyacentes entre sí. Por ejemplo, las siguientes cadenas citadas
'$HOME '
"isn't"
' where `<3'
"' is."
se pueden pegar juntas, formando una sola cadena larga después de la tokenización y eliminación de 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 saberlo.
Por otro lado, los scripts generalmente no deberían usarse ls
para nada. Para expandir un comodín, solo ... úsalo.
$ 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; printf
específicamente también funciona bien con múltiples argumentos stat
. Pero recorrer un comodín es un problema común, y con frecuencia se hace incorrectamente).
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 sepa exactamente lo que está haciendo".
Aquí hay una fórmula de tres puntos para citas en general:
Doble comillas
En contextos donde queremos suprimir la división de palabras y el globbing. También en contextos donde queremos que el literal sea tratado como una cadena, no como una expresión regular.
Comillas simples
En literales de cadena donde queremos suprimir la interpolación y el tratamiento especial de las barras invertidas. En otras palabras, las situaciones en las que usar comillas dobles sería inapropiado.
Sin comillas
En contextos en los que estamos absolutamente seguros de que no hay problemas de división de palabras o problemas globales o si queremos dividir palabras y problemas globales .
Ejemplos
Doble comillas
"StackOverflow rocks!"
, "Steve's Apple"
)"$var"
, "${arr[@]}"
)"$(ls)"
, "`ls`"
)"/my dir/"*
)"single'quote'delimited'string"
)"${filename##*/}"
)Comillas simples
'Really costs $$!'
, 'just a backslash followed by a t: \t'
)'The "crux"'
)$'\n\t'
)$'{"table": "users", "where": "first_name"=\'Steve\'}'
)Sin comillas
$$
, $?
, $#
etc.)((count++))
, "${arr[idx]}"
,"${string:start:length}"
[[ ]]
expresión interna que está libre de problemas de división de palabras y pegadura (esto es una cuestión de estilo y las opiniones pueden variar ampliamente)for word in $words
)for txtfile in *.txt; do ...
)~
ser interpretados como $HOME
( ~/"some dir"
pero no "~/some dir"
)Ver también:
"ls" "/"
La frase "todos los contextos de cadena" debe calificarse con más cuidado.
[[ ]]
, la cita sí importa en el lado derecho de =
/ ==
y =~
: hace la diferencia entre interpretar una cadena como un patrón / expresión regular o literalmente.
$'...'
) definitivamente deberían tener su propia sección.
"ls" "/"
lugar de las más comunes ls /
, y lo considero un defecto importante en las pautas.
case
:)
Por lo general, uso comillas como "$var"
seguro, a menos que esté seguro de que $var
no contiene espacio.
Lo uso $var
como una forma simple de unir líneas:
lines="`cat multi-lines-text-file.txt`"
echo "$lines" ## multiple lines
echo $lines ## all spaces (including newlines) are zapped
Para usar las variables en el script de shell, utilice "" las variables citadas, ya que la citada significa que la variable puede contener espacios o caracteres especiales que no afectarán la ejecución de su script de shell. De lo contrario, si está seguro de no tener espacios o caracteres especiales en el nombre de su variable, puede usarlos sin "".
Ejemplo:
echo "$ url name" - (Se puede usar en todo momento)
echo "$ url name" - (No se puede usar en tales situaciones, así que tenga precaución antes de usarlo)