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

La importancia de las comillas en Bash

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

Acerca de La importancia de las comillas

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

¡Usa comillas en tus variables!

¿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 separados.

Las comillas

Las comillas son el mecanismo principal para asegurar 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 archivo.

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 en 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 ver esto 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
# ..........^.........^

imprime

1 The quick brown fox jumps.

Pero sin comillas:

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

imprime

1 The
2 quick
3 brown
4 fox
5 jumps.

Veamos qué pasa con un valor diferente de $IFS:

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

imprime

first
second
third
Desactivar la división de palabras

Una técnica para desactivar la división de palabras es establecer $IFS en 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
# .........^.........^

imprime

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 archivo. Veremos cómo hacerlo más abajo.

Expansión de nombres de archivo

Los patrones «glob» se usan para expresar un patrón conciso que coincide con un conjunto de archivos. Por ejemplo, puedes hacer coincidir todos los archivos 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 exactamente con un carácter cualquiera,

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

    Advanced
    Hay muchos detalles sobre los conjuntos de caracteres. Haz clic aquí para mostrarlos.
    • [abc] coincide exactamente con un carácter que es una a, una b o una c.
      • para incluir un corchete de cierre literal en la expresión, haz que sea el primer carácter después del corchete de apertura (después de un ^ opcional)
        • [][] es un conjunto de caracteres que coincide con un corchete de cierre o uno de apertura.
        • o escápalo con una barra invertida: [[\]]
    • [x-y] coincide exactamente con 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 es 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 exactamente con un carácter que NO sea una a, una b o una c,
    • [^x-y] coincide exactamente con un carácter que NO esté en el rango de x a y
    • [[:character_class:]] coincide con un carácter que está en la «character_class» nombrada
      • 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: puntuación
        • cntrl: caracteres de control
        • graph: caracteres «visibles»
        • print: caracteres visibles más el espacio
      • se pueden combinar:
        • la clase [[:alpha:]] se compone de [[:lower:][:upper:]]
        • la clase [[:alnum:]] se compone de [[:alpha:][:digit:]]
        • la clase [[:word:]] se compone de [[:alnum:]_]
        • el guion bajo (octal 137) es tanto [[:word:]] como [[:punct:]]
        • la clase [[:graph:]] se compone de [[:alnum:][:punct:]]
        • la clase [[:print:]] se compone de [[:graph:] ]: solo el espacio (octal 040), ningún otro espacio en blanco.
Iterar sobre una lista de archivos

Puedes iterar sobre una lista de archivos con un bucle for

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

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

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 archivo donde el primer carácter es 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 regex y los conjuntos de caracteres de glob funcionan igual.
Advanced

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

Comillas y expansión de nombres de archivo

Hay ocasiones en las que quieres suprimir la expansión de nombres de archivo. Manejar la entrada del usuario es una de ellas. Considera este fragmento de código

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

Si el usuario escribe un asterisco (*), ¿qué se mostrará? La lista de archivos 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:]'

imprime

my puppet

Un error común es olvidar poner comillas en 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 archivos que coincidan. Supongamos que la persona que usa tu script tiene archivos llamados p, u y w en el directorio actual:

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

¡Esto da como resultado 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 archivos p y u con el primer patrón, y el archivo w con el segundo, y tr se invoca así

echo "$var" | tr p u w

Y esa es la cantidad equivocada de argumentos para tr.

Desactivar la expansión de nombres de archivo

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

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 archivo

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

  • shopt -s nocaseglob: realiza coincidencias sin distinguir mayúsculas de minúsculas
  • shopt -s extglob: habilita los patrones extendidos
  • shopt -s nullglob: si ningún archivo coincide, reemplaza el patrón por nada. El comportamiento predeterminado es dejar el patrón en su lugar como un string literal.
Editar en GitHub El enlace se abre en una ventana o pestaña nueva