He leído que necesita comillas dobles para expandir variables, por ejemplo
if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi
funcionará como se esperaba, mientras
if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi
siempre dirá $test ok
incluso si $test
es nulo.
pero entonces, ¿por qué no necesitamos citas echo $test
?
echo
argumento, se eliminarán los espacios adicionales y las nuevas líneas.Respuestas:
Siempre necesita comillas alrededor de las variables en todos los contextos de la lista , es decir, en todas partes, la variable puede expandirse a múltiples valores a menos que desee los 3 efectos secundarios de dejar una variable sin comillas.
Los contextos de lista incluyen argumentos a comandos simples como
[
oecho
, lasfor i in <here>
asignaciones a matrices ... Hay otros contextos donde las variables también necesitan ser citadas. Lo mejor es siempre citar variables a menos que tenga una muy buena razón para no hacerlo.Piense en la ausencia de comillas (en contextos de lista) como el operador split + glob .
Como si
echo $test
fueraecho glob(split("$test"))
.El comportamiento del shell es confuso para la mayoría de las personas porque en la mayoría de los otros idiomas, se colocan comillas alrededor de cadenas fijas, como
puts("foo")
, y no alrededor de variables (likeputs(var)
), mientras que en shell es al revés: todo es una cadena en shell, por lo que poner comillas alrededor de todo sería engorroso, ustedecho test
, no necesita hacerlo"echo" "test"
. En shell, las comillas se usan para otra cosa: evitar algún significado especial de algunos caracteres y / o afectar el comportamiento de algunas expansiones.En
[ -n $test ]
oecho $test
, el shell se dividirá$test
(en espacios en blanco por defecto), y luego realizará la generación de nombre de archivo (expandirá todos los*
patrones '?' ... a la lista de archivos coincidentes), y luego pasará esa lista de argumentos a los comandos[
oecho
.Nuevamente, piense en ello como
"[" "-n" glob(split("$test")) "]"
. Si$test
está vacío o contiene solo espacios en blanco (spc, tab, nl), entonces el operador split + glob devolverá una lista vacía, por lo[ -n $test ]
que será"[" "-n" "]"
, que es una prueba para verificar si "-n" es la cadena vacía o no. Pero imagina lo que hubiera pasado si$test
fuera "*" o "= foo" ...En
[ -n "$test" ]
,[
se pasa los cuatro argumentos"["
,"-n"
,""
y"]"
(sin las comillas), que es lo que queremos.Ya sea que haga
echo
o[
no la diferencia, es solo queecho
genera lo mismo, ya sea que haya pasado un argumento vacío o ningún argumento.Consulte también esta respuesta a una pregunta similar para obtener más detalles sobre el
[
comando y la[[...]]
construcción.fuente
La respuesta de @ h3rrmiller es buena para explicar por qué necesita las comillas para
if
(o mejor dicho,[
/test
), pero en realidad diría que su pregunta es incorrecta.Pruebe los siguientes comandos y verá lo que quiero decir.
Sin las comillas, la sustitución de variables hace que el segundo comando se expanda a:
y los múltiples espacios se colapsan en uno solo:
Con las comillas, se conservan los espacios.
Esto sucede porque cuando se cita un parámetro (si ese parámetro se pasa a
echo
,test
o algún otro comando), el valor de ese parámetro se envía como un valor a la orden. Si no lo cita, el shell hace su magia normal de buscar espacios en blanco para determinar dónde comienza y dónde termina cada parámetro.Esto también se puede ilustrar con el siguiente programa C (muy, muy simple). Pruebe lo siguiente en la línea de comando (es posible que desee hacerlo en un directorio vacío para no arriesgarse a sobrescribir algo).
y entonces...
Después de ejecutar
paramtest
,$?
mantendrá el número de parámetros que se pasó (y ese número se imprimirá).fuente
Esto se trata de cómo el shell interpreta la línea antes de ejecutar un programa.
Si la línea se lee
echo I am $USER
, el shell la expandeecho I am blrfl
yecho
no tiene idea de si el origen del texto es una expansión literal o variable. Del mismo modo, si se lee una líneaecho I am $UNDEFINED
, el shell se expandirá$UNDEFINED
en nada y los argumentos de echo lo seránI am
, y ese es el final. Dado queecho
funciona bien sin argumentos,echo $UNDEFINED
es completamente válido.Su problema con
if
no es realmente conif
, porqueif
solo ejecuta el programa y los argumentos que le siguen y ejecuta lathen
parte si el programa sale0
(o laelse
parte si hay una y el programa sale de otro modo0
):Cuando
if [ ... ]
haces una comparación, no estás usando primitivas integradas en el shell. En realidad, le está ordenando al shell que ejecute un programa llamado[
que es un superconjunto muy levetest(1)
que requiere que su último argumento sea]
. Ambos programas salen0
si la condición de prueba se cumplió y1
no fue así.La razón por la que algunas pruebas se rompen cuando una variable no está definida es porque
test
no ve que está usando una variable. Ergo, se[ $UNDEFINED -eq 2 ]
rompe porque cuando el shell termina con él, todos lostest
argumentos para ver son-eq 2 ]
, lo cual no es una prueba válida. Si lo hiciera con algo definido, como[ $DEFINED -ne 0 ]
, eso funcionaría porque el shell lo expandiría en una prueba válida (por ejemplo,0 -ne 0
).Hay una diferencia semántica entre
foo $UNDEFINED bar
, que se expande a dos argumentos (foo
ybar
) porque$UNDEFINED
está a la altura de su nombre. Compare esto confoo "$UNDEFINED" bar
, que se expande a tres argumentos (foo
, una cadena vacía y `bar). Las citas obligan al shell a interpretarlas como un argumento, ya sea que haya algo entre ellas o no.fuente
Sin comillas
$test
podría expandirse para ser más de una palabra, por lo que debe ser citado para no romper la sintaxis ya que cada interruptor dentro del[
comando espera un argumento que es lo que hacen las comillas (lo que se$test
expande en un argumento)La razón por la que no necesita comillas para expandir una variable
echo
es porque no espera un argumento. Simplemente imprimirá lo que le digas. Entonces, incluso si se$test
expande a 100 palabras, echo aún lo imprimirá.Echa un vistazo a las trampas de Bash
fuente
echo
?echo
. ¿Qué te hace pensar lo contrario?echo $test
y funciona (genera el valor de $ test)Los parámetros vacíos se eliminan si no se citan:
El comando llamado no ve que haya un parámetro vacío en la línea de comando del shell. Parece que [se define para devolver 0 para -n sin nada siguiente. Por qué siempre.
Las citas también marcan la diferencia para echo, en varios casos:
fuente
echo
, es la cáscara. Verías el mismo comportamiento conls
. Pruebetouch '*'
algo de tiempo si se siente aventurero.:)