Tracks
/
Bash
Bash
/
Temario
/
La importancia de las comillas
La

La importancia de las comillas en Bash

{one: "1 ejercicio", many: "%{count} ejercicios", other: "%{count} ejercicios"}

Acerca de La importancia de las comillas

Un consejo que los programadores noveles de bash escucharán una y otra vez:

¡Pon comillas a tus variables!

¿Y por qué?

Cuando expandes una variable (como $name), casi siempre quieres usar el valor de la variable como un solo string (o número). No quieres que el valor se trate de forma inesperada como dos o más strings distintos.

Las comillas

Poner comillas es el mecanismo principal para garantizar que un fragmento de texto se trate como una sola palabra. Hay dos tipos principales de comillas.

Comillas dobles

Las comillas dobles permiten la expansión de parámetros, la expansión aritmética y la sustitución de comandos dentro de ellas.

echo "This is bash version $BASH_VERSION"

Puedes incluir una comilla doble literal dentro de un string entre comillas dobles si la precedes con una barra invertida.

echo "She said \"Let me teach you about bash.\""

Comillas simples

Las comillas simples no permiten ninguna expansión dentro de ellas.

echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION

No es posible incrustar una comilla simple dentro de un string entre comillas simples.

La importancia de las comillas

¿Qué tiene de malo echo $name, exactamente?

Cuando el shell analiza una línea de código, realiza varios tipos de expansiones, en orden. Dos de las últimas expansiones son la división de palabras y la expansión de nombres de fichero.

División de palabras

Cualquier resultado de la expansión de parámetros, la sustitución de comandos o la expansión aritmética que no haya ocurrido dentro de comillas dobles es candidato a la «división de palabras». El shell toma el resultado expandido y lo divide por cualquier secuencia de caracteres presente en el valor de $IFS. De forma predeterminada, son el espacio, el tabulador y el salto de línea.

Podemos verlo en acción con un bucle del shell:

sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^

da como resultado:

1 The quick brown fox jumps.

Pero sin comillas:

i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

da como resultado:

1 The
2 quick
3 brown
4 fox
5 jumps.

Veámoslo con otro valor de $IFS:

csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done

da como resultado:

first
second
third
Desactivar la división de palabras

Una técnica para desactivar la división de palabras es asignar a $IFS un string vacío

sentence="The quick brown fox jumps."
IFS=    # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

da como resultado:

1 The quick brown fox jumps.

Sin embargo, no se recomienda dejar las variables sin comillas a menos que también desactives la expansión de nombres de fichero. Veremos cómo hacerlo más abajo.

Expansión de nombres de fichero

Los patrones «glob» sirven para expresar de forma concisa un patrón que coincida con un conjunto de ficheros. Por ejemplo, puedes hacer coincidir todos los ficheros Markdown de un directorio con *.md. Los patrones glob existen desde la invención de Unix.

Los caracteres comodín de glob son:

  • * coincide con cero o más caracteres cualesquiera,

  • ? coincide con exactamente un carácter cualquiera,

  • [...] coincide con exactamente un carácter del conjunto de caracteres indicado entre los corchetes.

    Advanced
    Hay un montón de detalles sobre los conjuntos de caracteres. Haz clic aquí para mostrarlos.
    • [abc] coincide con exactamente un carácter que sea una a, una b o una c.
      • para incluir un corchete de cierre literal en la expresión, colócalo como primer carácter después del corchete de apertura (después de un ^ opcional)
        • [][] es un conjunto de caracteres que coincidirá con un corchete de cierre o uno de apertura.
        • o escápalo con una barra invertida: [[\]]
    • [x-y] coincide con exactamente un carácter que esté en el rango de x a y
      • para incluir un guion literal en un conjunto de caracteres, debe ser el primer carácter después del corchete de apertura (después de un ^ opcional) o el último carácter antes del corchete de cierre.
        • [-xy] o [xy-] coinciden cada uno con exactamente un carácter que sea un -, una x o una y
        • o escápalo con una barra invertida: [x\-y]
      • se pueden especificar varios rangos; por ejemplo, [0-9A-Fa-f] coincide con un dígito hexadecimal
    • [^abc] coincide con exactamente un carácter que NO sea una a, una b o una c,
    • [^x-y] coincide con exactamente un carácter que NO esté en el rango de x a y
    • [[:character_class:]] coincide con un carácter que pertenezca a la «character_class» indicada
      • las clases de caracteres disponibles son:
        • lower -- letras minúsculas
        • upper -- letras mayúsculas
        • alpha -- letras
        • digit -- dígitos
        • alnum -- letras y dígitos
        • space -- espacios en blanco
        • blank -- espacios en blanco horizontales (espacio y tabulador)
        • xdigit -- dígitos hexadecimales
        • word -- caracteres permitidos en un identificador (letras, números, guion bajo)
        • punct -- signos de puntuación
        • cntrl -- caracteres de control
        • graph -- caracteres «visibles»
        • print -- caracteres visibles más el espacio
      • se pueden combinar:
        • la clase [[:alpha:]] está formada por [[:lower:][:upper:]]
        • la clase [[:alnum:]] está formada por [[:alpha:][:digit:]]
        • la clase [[:word:]] está formada por [[:alnum:]_]
        • el guion bajo (octal 137) pertenece tanto a [[:word:]] como a [[:punct:]]
        • la clase [[:graph:]] está formada por [[:alnum:][:punct:]]
        • la clase [[:print:]] está formada por [[:graph:] ] -- solo el espacio (octal 040), no ningún otro espacio en blanco.
Iterar sobre una lista de ficheros

Puedes iterar sobre una lista de ficheros con un bucle for

for file in *.csv; do
    do_something_with "$file"
done

Fíjate en que la variable "$file" está entre comillas. El patrón glob puede devolver ficheros cuyo nombre contenga espacios. Necesitamos poner comillas a la variable para que el comando do_something_with reciba exactamente un argumento, el nombre del fichero.

Los patrones glob no son expresiones regulares

Los patrones glob no son expresiones regulares.

  • coincidir con exactamente un carácter
    • regex .
    • glob ?
  • coincidir con cero o más caracteres
    • regex .*
    • glob *
      • el patrón glob .* significa «coincidir con un nombre de fichero en el que el primer carácter sea un punto literal seguido de cero o más caracteres cualesquiera».
  • coincidir con uno o más caracteres
    • regex .+
    • glob ?*
  • los conjuntos de caracteres de las regex y los de glob funcionan igual.
Advanced

Bash ofrece patrones extendidos que se acercan más a las expresiones regulares.

Las comillas y la expansión de nombres de fichero

Hay ocasiones en las que quieres suprimir la expansión de nombres de fichero. Gestionar la entrada del usuario es una de ellas. Fíjate en este fragmento de código

read -p "Enter something: " user_input
echo $user_input

Si el usuario introduce un asterisco (*), ¿qué se mostrará como salida? La lista de ficheros del directorio actual. Probablemente eso no sea lo que quieres hacer.

Otro ejemplo es pasar patrones como argumentos a programas que esperan patrones. El programa tr se usa a menudo para manipular texto. Para convertir a minúsculas el valor de una variable, puedes hacer esto

var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'

da como resultado:

my puppet

Un error habitual es olvidarse de poner comillas a los argumentos de tr: tr [:upper:] [:lower:]

Caution

¿Por qué es un problema? Los patrones glob sin comillas se expandirán a la lista de ficheros que coincidan. Supón que la persona que usa tu script tiene ficheros llamados p, u y w en el directorio actual:

touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]

¡Esto da lugar a un error!

El patrón [:upper:] sin comillas coincide con cualquiera de :, u, p, e o r. El patrón [:lower:] sin comillas coincide con cualquiera de :, l, o, w, e o r. Bash hace coincidir los ficheros p y u con el primer patrón, y el fichero w con el segundo, y se invoca tr así

echo "$var" | tr p u w

Y ese no es el número correcto de argumentos para tr.

Desactivar la expansión de nombres de fichero

Puedes usar el comando set para desactivar la expansión de nombres de fichero.

echo *      # prints a list of files
set -f      # disable filename expansion
echo *      # prints a literal asterisk
set +f      # enable filename expansion
Controlar la expansión de nombres de fichero

Hay varias opciones del comando integrado shopt que controlan cómo funciona la expansión de nombres de fichero. Un par de las interesantes son:

  • shopt -s nocaseglob -- realiza coincidencias sin distinguir mayúsculas y minúsculas
  • shopt -s extglob -- activa los patrones extendidos
  • shopt -s nullglob -- si no coincide ningún fichero, sustituye el patrón por nada. El comportamiento predeterminado es dejar el patrón tal cual, como string literal.
Editar en GitHub El enlace se abre en una nueva ventana o pestaña