world script run hello comando bash shell sh quoting

bash - hello - run script linux



Acabo de asignar una variable, pero echo $ variable muestra algo más (6)

Aquí hay una serie de casos en los que echo $var puede mostrar un valor diferente al que se acaba de asignar. Esto sucede independientemente de si el valor asignado fue "comillas dobles", "comillas simples" o sin comillas.

¿Cómo consigo que el shell establezca mi variable correctamente?

Asteriscos

El resultado esperado es /* Foobar is free software */ , pero en cambio 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 [az] , ¡pero a veces recibo una sola letra!

$ var=[a-z] $ echo $var c

Saltos de línea (líneas nuevas)

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, múltiples espacios desaparecen o se colapsan en uno!

$ var=" title | count" $ echo $var title | count

Pestañas

¡Esperaba dos valores separados por tabuladores, pero en cambio obtengo dos valores separados por espacios!

$ var=$''key/tvalue'' $ echo $var key value


Además de otros problemas causados ​​por la falta de comillas, -n y -e pueden ser consumidos por echo como argumentos. (Solo el primero es legal según la especificación POSIX para echo , pero varias implementaciones comunes violan la especificación y también consumen -e ).

Para evitar esto, use printf lugar de echo cuando los detalles importen.

Así:

$ vars="-e -n -a" $ echo $vars # breaks because -e and -n can be treated as arguments to echo -a $ echo "$vars" -e -n -a

Sin embargo, las citas correctas no siempre te salvarán cuando uses echo :

$ vars="-n" $ echo $vars $ ## not even an empty line was printed

... mientras que te salvará con printf :

$ vars="-n" $ printf ''%s/n'' "$vars" -n


Además de poner la variable entre comillas, también se podría traducir el resultado de la variable usando tr y convirtiendo espacios en líneas nuevas.

$ echo $var | tr " " "/n" foo bar baz

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 la matriz.


En todos los casos anteriores, la variable se establece correctamente, ¡pero no se lee correctamente! La forma correcta es usar comillas dobles al hacer referencia a :

echo "$var"

Esto da el valor esperado en todos los ejemplos dados. ¡Siempre cita referencias variables!

¿Por qué?

Cuando una variable no está entre comillas , hará lo siguiente:

  1. Someterse a la división de campos donde el valor se divide en varias palabras en espacios en blanco (por defecto):

    Antes: /* Foobar is free software */

    Después: /* , Foobar , is , free , software , */

  2. Cada una de estas palabras sufrirá una expansión de nombre de ruta , donde los patrones se expanden en archivos coincidentes:

    Antes: /*

    Después: /bin , /boot , /dev , /etc , /home , ...

  3. Finalmente, todos los argumentos se pasan a echo, que los escribe separados por espacios individuales , dando

    /bin /boot /dev /etc /home Foobar is free software Desktop/ Downloads/

    en lugar del valor de la variable.

Cuando se cita la variable:

  1. Ser sustituido por su valor.
  2. No hay paso 2.

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 del nombre de ruta. Las herramientas como shellcheck están ahí para ayudar y advertirán sobre las citas faltantes en todos los casos anteriores.


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 en espacios en blanco u otros caracteres especiales? escrito por Gilles en Unix y Linux :

¿Por qué necesito escribir "$foo" ? ¿Qué pasa sin las comillas?

$foo no significa "tomar el valor de la variable foo ". Significa algo mucho más complejo:

  • Primero, tome el valor de la variable.
  • División de campos: trate ese valor como una lista de campos separados por espacios en blanco y cree la lista resultante. Por ejemplo, si la variable contiene foo * bar ​ el resultado de este paso es la lista de 3 elementos foo , * , bar .
  • Generación de nombre de archivo: trate cada campo como un globo, es decir, como un patrón comodín, y reemplácelo por la lista de nombres de archivo que coinciden con este patrón. Si el patrón no coincide con ningún archivo, no se modifica. En nuestro ejemplo, esto da como resultado la lista que contiene foo , seguida de la lista de archivos en el directorio actual y finalmente bar . Si el directorio actual está vacío, el resultado es foo , * , bar .

Tenga en cuenta que el resultado es una lista de cadenas. Hay dos contextos en la sintaxis de shell: contexto de lista y contexto de cadena. La división de campos y la generación de nombre de archivo solo ocurren en el contexto de la lista, pero eso es la mayor parte del tiempo. Las comillas dobles delimitan un contexto de cadena: la cadena completa entre comillas dobles es una sola cadena, que no se debe dividir. (Excepción: "$@" para expandir a la lista de parámetros posicionales, por ejemplo, "$@" es equivalente a "$1" "$2" "$3" si hay tres parámetros posicionales. Consulte ¿Cuál es la diferencia entre $ * y $ @? )

Lo mismo sucede con la sustitución de comandos con $(foo) o con `foo` . Como nota al margen, no use `foo` : sus reglas de cotización son extrañas y no portátiles, y todos los shells modernos admiten $(foo) que es absolutamente equivalente, excepto por tener reglas de cotización intuitivas.

La salida de la sustitución aritmética también experimenta las mismas expansiones, pero eso normalmente no es una preocupación, ya que solo contiene caracteres no expandibles (suponiendo que IFS no contenga dígitos o - ).

Ver ¿ Cuándo es necesaria la doble cita? para obtener más detalles sobre los casos en que puede omitir las citas.

A menos que quiera que suceda todo este rigmarole, solo recuerde usar siempre comillas dobles alrededor de las sustituciones de variables y comandos. Tenga cuidado: omitir las comillas puede conducir no solo a errores, sino también a agujeros de seguridad .


cita doble del usuario para obtener el valor exacto. Me gusta esto:

echo "${var}"

y leerá su valor correctamente.


echo $var salida echo $var depende en gran medida del valor de la variable IFS . Por defecto contiene espacio, tabulación y caracteres de nueva línea:

[ks@localhost ~]$ echo -n "$IFS" | cat -vte ^I$

Esto significa que cuando Shell realiza la división de campos (o división de palabras), utiliza todos estos caracteres como separadores de palabras. Esto es lo que sucede cuando se hace referencia a una variable sin comillas dobles para repetirla ( $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 en nulo. Ver http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :

Si el valor de IFS es nulo, no se realizará división de campo.

Establecer en nulo significa establecer en valor vacío:

IFS=

Prueba:

[ks@localhost ~]$ echo -n "$IFS" | cat -vte ^I$ [ks@localhost ~]$ var=$''key/nvalue'' [ks@localhost ~]$ echo $var key value [ks@localhost ~]$ IFS= [ks@localhost ~]$ echo $var key value [ks@localhost ~]$