A continuación, se muestran una serie de casos en los que se echo $var
puede mostrar un valor diferente al que se acaba de asignar. Esto sucede independientemente de si el valor asignado fue "entre comillas dobles", "entre comillas simples" o no.
¿Cómo consigo que el shell establezca mi variable correctamente?
Asteriscos
La salida esperada es /* Foobar is free software */
, pero en su lugar obtengo una lista de nombres de archivo:
$ var="/* Foobar is free software */"
$ echo $var
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...
Corchetes
El valor esperado es [a-z]
, ¡pero a veces obtengo una sola letra!
$ var=[a-z]
$ echo $var
c
Saltos de línea (nuevas líneas)
El valor esperado es una lista de líneas separadas, ¡pero en cambio todos los valores están en una línea!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
Múltiples espacios
Esperaba un encabezado de tabla cuidadosamente alineado, pero en su lugar, varios espacios desaparecen o se contraen en uno.
$ var=" title | count"
$ echo $var
title | count
Pestañas
Esperaba dos valores separados por tabulaciones, ¡pero en su lugar obtengo dos valores separados por espacios!
$ var=$'key\tvalue'
$ echo $var
key value
var=$(cat file)
está bien, peroecho "$var"
es necesario.Respuestas:
En todos los casos anteriores, la variable está configurada correctamente, ¡pero no se lee correctamente! La forma correcta es usar comillas dobles al hacer referencia :
Esto da el valor esperado en todos los ejemplos dados. ¡Cite siempre referencias de variables!
¿Por qué?
Cuando una variable no está entre comillas , hará lo siguiente:
Realice la división de campos donde el valor se divide en varias palabras en espacios en blanco (por defecto):
Antes de:
/* Foobar is free software */
Después:
/*
,Foobar
,is
,free
,software
,*/
Cada una de estas palabras experimentará una expansión de nombre de ruta , donde los patrones se expandirán en archivos coincidentes:
Antes de:
/*
Después:
/bin
,/boot
,/dev
,/etc
,/home
, ...Finalmente, todos los argumentos se pasan a echo, que los escribe separados por espacios simples , dando
en lugar del valor de la variable.
Cuando se cite la variable :
Es por eso que siempre debe citar todas las referencias de variables , a menos que requiera específicamente la división de palabras y la expansión de la ruta. Herramientas como shellcheck están ahí para ayudar y advertirán sobre comillas faltantes en todos los casos anteriores.
fuente
$(..)
elimina los avances de línea finales. Puede utilizarlovar=$(cat file; printf x); var="${var%x}"
para solucionarlo.Es posible que desee saber por qué sucede esto. Junto con la gran explicación de ese otro tipo , encuentre una referencia de ¿Por qué mi script de shell se ahoga con espacios en blanco u otros caracteres especiales? escrito por Gilles en Unix y Linux :
fuente
Además de otros problemas causados por no citar,
-n
y-e
pueden consumirseecho
como argumentos. (Solo el primero es legal según la especificación POSIXecho
, pero varias implementaciones comunes violan la especificación y también consumen-e
).Para evitar esto, utilice en
printf
lugar deecho
cuando los detalles importan.Así:
Sin embargo, las citas correctas no siempre lo salvarán al usar
echo
:... mientras que te salvará con
printf
:fuente
-e
/-n
/ barra invertida no aparece?" Podemos agregar enlaces desde aquí según corresponda.-n
también ?-e
. El estándar paraecho
no especifica la salida cuando su primer argumento es-n
, lo que hace que cualquier salida posible sea legal en ese caso; no existe tal provisión para-e
.comillas dobles del usuario para obtener el valor exacto. Me gusta esto:
y leerá su valor correctamente.
fuente
echo $var
la salida depende en gran medida del valor de laIFS
variable. De forma predeterminada, contiene espacios, tabulaciones y caracteres de nueva línea:Esto significa que cuando shell está dividiendo campos (o palabras), usa todos estos caracteres como separadores de palabras. Esto es lo que sucede cuando se hace referencia a una variable sin comillas dobles para hacerla eco (
$var
) y, por lo tanto, se altera la salida esperada.Una forma de evitar la división de palabras (además de usar comillas dobles) es establecer
IFS
un valor nulo. Ver http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :Establecer en nulo significa establecer en valor vacío:
Prueba:
fuente
set -f
tendríasIFS
valor nulo,echo $var
se expandiráecho '/* Foobar is free software */'
ay la expansión de ruta no se realiza dentro de cadenas entre comillas simples.mkdir "/this thing called Foobar is free software etc/"
ves que todavía se expande. Obviamente, es más práctico para el[a-z]
ejemplo.[a-z]
ejemplo.La respuesta de ks1322 me ayudó a identificar el problema mientras usaba
docker-compose exec
:Si omite la
-T
bandera,docker-compose exec
agregue un carácter especial que rompa la salida, vemos enb
lugar de1b
:Con
-T
bandera,docker-compose exec
funciona como se esperaba:fuente
Además de poner la variable entre comillas, también se podría traducir la salida de la variable usando
tr
y convirtiendo espacios en nuevas líneas.Aunque esto es un poco más complicado, agrega más diversidad con la salida, ya que puede sustituir cualquier carácter como separador entre las variables de matriz.
fuente
tr
al revés para crear matrices a partir de archivos de texto.tr
necesario crear correctamente / correctamente una matriz a partir de un archivo de texto; puede especificar el separador que desee configurando IFS. Por ejemplo:IFS=$'\n' read -r -d '' -a arrayname < <(cat file.txt && printf '\0')
funciona desde bash 3.2 (la versión más antigua en circulación) y establece correctamente el estado de salida en falso sicat
falla. Y si quisiera, digamos, pestañas en lugar de nuevas líneas, simplemente reemplazaría$'\n'
por$'\t'
.arrayname=( $( cat file | tr '\n' ' ' ) )
, entonces eso está roto en múltiples capas: está englobando sus resultados (por lo que a se*
convierte en una lista de archivos en el directorio actual), y funcionaría igual de bien sin eltr
( o elcat
, para el caso; uno podría simplemente usararrayname=$( $(<file) )
y se rompería de la misma manera, pero de manera menos ineficiente).