Terminábamos la lección anterior con una afirmación que parecía una curiosidad y es en realidad la clave de esta lección: cuando escribes rm *.csv, no es rm quien entiende el asterisco. Es Bash quien lo expande antes de ejecutar nada, y rm recibe ya la lista completa de nombres. Entender ese reparto de responsabilidades explica comportamientos que de otro modo parecen mágicos o arbitrarios, y evita accidentes que han costado sistemas enteros. Aquí aprenderás a seleccionar ficheros por patrones con precisión sobre /var/log/veloz y el histórico de /srv/veloz/datos.

Contenido

  1. Qué es el globbing y quién lo hace
  2. Los comodines básicos: *, ? y [...]
  3. Clases de caracteres POSIX
  4. Expansión de llaves: {}
  5. Globbing frente a expresiones regulares
  6. Ajustar el comportamiento con shopt
  7. Qué ocurre cuando un patrón no casa con nada
  8. Peligros clásicos
  9. Casos prácticos sobre los datos de Veloz Envíos

  1. Qué es el globbing y quién lo hace

El globbing (o expansión de rutas) es el mecanismo por el que el shell sustituye un patrón por la lista ordenada de nombres de fichero existentes que encajan con él. Ocurre durante la fase de expansiones que estudiaste en 01-04, justo antes de buscar y ejecutar el comando.

La demostración definitiva es echo, que se limita a imprimir los argumentos que recibe:

cd /srv/veloz/datos/historico/2026/08
echo *.csv
envios-2026-08-01.csv envios-2026-08-02.csv envios-2026-08-03.csv

echo no sabe nada de comodines. Recibió tres argumentos ya expandidos, porque Bash reemplazó el patrón antes de invocarlo. Exactamente lo mismo ocurre con ls, cp o rm: todos reciben la lista final.

Tres consecuencias que debes interiorizar:

  • El patrón solo casa con ficheros que existen. Si escribes *.log en un directorio sin ficheros .log, no hay nada que sustituir (sección 7).
  • La expansión ocurre en el directorio actual, salvo que el patrón incluya una ruta: /var/log/veloz/*.log funciona desde cualquier sitio.
  • Las comillas desactivan el globbing. echo "*.csv" imprime literalmente *.csv, porque el patrón entrecomillado no se expande. Ese es el motivo por el que grep "*.csv" fichero no hace lo que esperas y find . -name "*.csv" sí: en find queremos que el patrón llegue sin expandir al programa.

  1. Los comodines básicos: *, ? y [...]

Patrón Casa con Ejemplo
* Cualquier secuencia de caracteres, incluida la vacía envios*envios.csv, envios-2026-08-01.csv
? Exactamente un carácter cualquiera envios-2026-08-0?.csv → días 01 a 09
[abc] Un carácter de los listados log[123].txt
[a-z] Un carácter del rango [a-m]* → nombres que empiezan de la a a la m
[!0-9] Un carácter que no esté en el conjunto [!e]* → nombres que no empiezan por e
ls /var/log/veloz/*.log                  # todos los logs
ls envios-2026-08-0?.csv                 # los nueve primeros días del mes
ls envios-2026-0[78]-*.csv               # solo julio y agosto
ls envios-2026-08-[12][0-9].csv          # días 10 a 29

Dos matices importantes:

  • * no cruza barras. /var/log/* lista lo que hay en /var/log, pero no desciende a sus subdirectorios. Para eso está globstar (sección 6).
  • * no casa con los ficheros ocultos (los que empiezan por .). ls * no muestra .bashrc. Es una protección deliberada: sin ella, rm * en tu $HOME borraría toda tu configuración. Se cambia con dotglob, pero rara vez conviene.

En [!0-9], el signo ! niega el conjunto. También se acepta ^ en Bash, pero ! es la forma estándar y portable.

  1. Clases de caracteres POSIX

Escribir [a-zA-Z] funciona en inglés, pero se rompe con acentos y depende de la configuración regional. Las clases POSIX son la forma robusta:

Clase Casa con
[[:digit:]] Dígitos 0-9
[[:alpha:]] Letras, incluidas las acentuadas del idioma activo
[[:alnum:]] Letras y dígitos
[[:upper:]] / [[:lower:]] Mayúsculas / minúsculas
[[:space:]] Espacios, tabuladores y saltos de línea
[[:punct:]] Signos de puntuación
ls envios-[[:digit:]][[:digit:]][[:digit:]][[:digit:]]-*.csv   # año de 4 dígitos
ls informe-[[:upper:]]*                                        # empieza por mayúscula

Fíjate en el doble corchete: la clase es [:digit:] y va dentro de un conjunto [...], de ahí [[:digit:]]. Puedes combinarlas: [[:digit:]_-] casa con un dígito, un guion bajo o un guion.

  1. Expansión de llaves: {}

La expansión de llaves se parece al globbing, pero es otra cosa distinta y más poderosa: genera cadenas de texto sin consultar el disco y ocurre antes que el globbing, en la primera fase de expansiones.

echo {1..5}                # → 1 2 3 4 5
echo {ene,feb,mar}         # → ene feb mar
echo envios-2026-0{7,8}    # → envios-2026-07 envios-2026-08

Ninguno de esos nombres tiene por qué existir: la expansión de llaves fabrica texto, no selecciona ficheros. Por eso sirve para crear cosas, mientras que el globbing solo sirve para seleccionar lo que ya está:

mkdir -p ~/veloz-ops/{bin,lib,etc,logs}          # crea 4 directorios
mkdir -p /srv/veloz/datos/historico/2026/{01..12} # crea los 12 meses, con cero delante

El rango admite paso: {1..20..5} da 1 6 11 16, y también funciona con letras: {a..e}.

El idiom más útil de todos es fichero{,.bak}, que se expande a fichero fichero.bak:

cp -a ~/veloz-ops/etc/veloz-ops.conf{,.bak}

Bash convierte eso en cp -a ~/veloz-ops/etc/veloz-ops.conf ~/veloz-ops/etc/veloz-ops.conf.bak. Es la forma abreviada de la copia de seguridad rápida de la lección 02-01, y la verás constantemente en documentación y en el trabajo de gente experimentada. Funciona porque la primera alternativa está vacía (nada entre { y ,).

Una advertencia: la expansión de llaves no se produce si hay espacios dentro ni si solo hay un elemento. echo {a} imprime {a} literal.

  1. Globbing frente a expresiones regulares

*.csv como patrón de globbing y .*\.csv como expresión regular hacen lo mismo, pero escritos de forma distinta y con reglas incompatibles. Confundirlos es un error clásico, porque los mismos símbolos significan cosas diferentes.

Aspecto Globbing (comodines) Expresiones regulares
Quién lo interpreta El shell, antes de ejecutar El programa (grep, sed, awk)
Sobre qué actúa Nombres de fichero existentes Texto de cualquier procedencia
* Cualquier secuencia de caracteres «Cero o más veces lo anterior»
? Exactamente un carácter «Cero o una vez lo anterior» (en ERE)
. Un punto literal Un carácter cualquiera
Un carácter cualquiera ? .
Cualquier secuencia * .*
Coincidencia Debe casar el nombre completo Basta con que case una parte
ls *.log                                   # globbing: ficheros terminados en .log
grep '^2026-08-03.*ERROR' /var/log/veloz/app.log   # regex: líneas del día con ERROR

En el segundo caso, las comillas simples son imprescindibles: impiden que el shell intente expandir * como comodín y garantizan que la expresión llegue intacta a grep. Las expresiones regulares completas se estudian en 05-04; por ahora basta con que distingas el terreno de cada una.

  1. Ajustar el comportamiento con shopt

shopt (shell options) activa (-s) y desactiva (-u) opciones de Bash que cambian cómo funciona el globbing.

Opción Qué hace
nullglob Si el patrón no casa, se expande a nada en lugar de quedarse literal
failglob Si el patrón no casa, el comando no se ejecuta y se muestra un error
dotglob Incluye los ficheros ocultos en las expansiones
nocaseglob Ignora mayúsculas y minúsculas al casar
globstar Habilita ** para recorrer subdirectorios recursivamente
extglob Habilita patrones extendidos como !(*.bak) o `+(a
shopt -s globstar
ls /var/log/veloz/**/*.log     # todos los .log a cualquier profundidad
shopt              # consultar el estado de todas las opciones

globstar merece atención: ** recorre el árbol completo, así que **/*.csv encuentra los CSV de todos los subdirectorios del histórico sin necesidad de find. Ten cuidado con él en árboles enormes, porque puede tardar mucho.

nocaseglob resuelve un caso real: si algún fichero llegó como Envios-2026-08-01.CSV, con la opción activa *.csv lo encontrará igualmente.

Estas opciones son persistentes en la sesión: se activan en ~/.bashrc para el uso interactivo o al principio de un script para que su comportamiento sea explícito.

  1. Qué ocurre cuando un patrón no casa con nada

Este es el comportamiento por defecto más contraintuitivo de Bash:

cd /srv/veloz/datos
echo *.xml
*.xml

No hay ningún fichero .xml, y en lugar de devolver una lista vacía, Bash deja el patrón tal cual y lo pasa como argumento literal. Es una herencia histórica y la causa de errores desconcertantes: ls *.xml responde ls: no se puede acceder a '*.xml': No existe el fichero o el directorio, mencionando un fichero llamado literalmente *.xml que evidentemente no existe. Peor aún es lo que ocurre en un bucle:

for f in *.xml; do echo "procesando $f"; done
procesando *.xml

El bucle se ejecuta una vez con el patrón sin expandir como si fuera un nombre de fichero, en lugar de no ejecutarse ninguna. Si dentro hubiera un rm "$f" o una lectura del fichero, el resultado sería un error incomprensible. Los bucles se estudian en 04-01, pero el problema es de globbing y su solución también:

shopt -s nullglob
for f in *.xml; do echo "procesando $f"; done   # no imprime nada: correcto

Con nullglob, un patrón sin coincidencias se expande a cero argumentos y el bucle no itera. Es la opción que debes activar en cualquier script que recorra ficheros por patrón. La alternativa failglob es aún más estricta: aborta el comando con un error explícito, útil cuando la ausencia de ficheros indica un problema real.

  1. Peligros clásicos

El espacio de más en rm *. Este es el accidente más famoso de Unix:

rm *.bak     # borra las copias de seguridad
rm * .bak    # ← borra TODO el directorio, y luego se queja de que no existe ".bak"

Un solo espacio convierte «borra los .bak» en «borra todo». rm procesa los argumentos en orden, así que para cuando muestra el error sobre .bak ya ha borrado el contenido del directorio. Costumbre que lo evita: sustituir rm por ls y revisar la lista antes.

ls *.bak     # ¿es exactamente lo que quiero borrar?
rm *.bak

Ficheros que empiezan por guion. Si el directorio contiene un fichero llamado -rf, la expansión de rm * lo coloca entre los argumentos y rm lo interpreta como una opción. La protección, ya vista en 02-01, es rm -- * o rm ./*.

rm -rf $DIR/* con la variable vacía. Si DIR no está definida, el shell expande la línea a rm -rf /*. Es la causa documentada de varios borrados catastróficos en producción, y se previene con las técnicas de comillas y valores por defecto del Módulo 3.

Comodines en rutas absolutas amplias. rm /var/log/veloz/* parece acotado, pero si escribes /var/log/* por error el alcance cambia por completo. Cuanto más arriba esté el directorio, más despacio hay que escribir.

  1. Casos prácticos sobre los datos de Veloz Envíos

# 1. Ver todos los logs de la aplicación, incluidos los rotados
ls -lh /var/log/veloz/*.log*

# 2. Contar los CSV archivados de agosto de 2026
ls /srv/veloz/datos/historico/2026/08/envios-2026-08-??.csv | wc -l

# 3. Copiar los datos de la primera quincena a un directorio de análisis
mkdir -p ~/veloz-ops/tmp/quincena
cp -a /srv/veloz/datos/historico/2026/08/envios-2026-08-{01..15}.csv \
      ~/veloz-ops/tmp/quincena/

# 4. Buscar ERROR en todos los logs del árbol (requiere globstar)
shopt -s globstar
grep -l ERROR /var/log/veloz/**/*.log

# 5. Copia de seguridad de toda la configuración del toolkit
cp -a ~/veloz-ops/etc/veloz-ops.conf{,.$(date +%F).bak}

El caso 3 mezcla los dos mecanismos y es un buen resumen de la lección: {01..15} es expansión de llaves, que genera quince nombres de texto sin mirar el disco; a continuación esos nombres se resuelven contra ficheros reales. Si alguno de los quince días no existiera, cp daría un error por ese fichero concreto —a diferencia de un patrón *, que simplemente no lo habría incluido—. Ese comportamiento es una ventaja: te avisa de que falta un día en el histórico, que es justo lo que querrías saber.

Errores Comunes y Consejos

  • Creer que el comando interpreta el comodín. Lo hace el shell. Compruébalo siempre con echo delante antes de una operación destructiva.
  • Entrecomillar un patrón que querías expandir, o al revés. rm "*.log" busca un fichero llamado literalmente *.log; find . -name *.log sin comillas falla porque el shell lo expande antes de que find lo vea.
  • Esperar que * incluya los ocultos. No lo hace, y eso es bueno. Si de verdad los necesitas, shopt -s dotglob.
  • Confundir * de globbing con * de expresión regular. En un patrón de fichero es «cualquier cosa»; en una regex es «cero o más repeticiones del elemento anterior».
  • Escribir bucles sobre patrones sin nullglob. Iterarán una vez sobre el patrón literal cuando no haya coincidencias.
  • Usar ** sin globstar. Sin la opción activada, ** se comporta exactamente igual que * y no recorre subdirectorios; el fallo es silencioso.
  • Un espacio de más antes del comodín. Revisa dos veces cualquier línea que combine rm y *.

Ejercicios

Ejercicio 1 — Seleccionar con precisión. En /srv/veloz/datos/historico/2026/08 hay ficheros envios-2026-08-01.csv a envios-2026-08-31.csv, más un envios-2026-08-03.csv.bak y un notas.txt. Escribe el patrón que selecciona: (a) todos los CSV del mes, sin el .bak; (b) solo los días del 20 al 29; (c) todos los ficheros del mes que no empiecen por e; (d) los días acabados en 1 o 5.

Ejercicio 2 — Llaves frente a comodines. Explica la diferencia de resultado entre estos dos comandos en un directorio que contiene solo envios-2026-08-01.csv y envios-2026-08-02.csv, y di cuál usarías para detectar un día que falta en el histórico:

ls envios-2026-08-0*.csv
ls envios-2026-08-0{1..5}.csv

Ejercicio 3 — Preparar un script seguro. Vas a escribir un script que archive todos los ficheros .log de ~/veloz-ops/logs. Indica qué opciones de shopt activarías y por qué, y muestra cómo comprobarías sin riesgo qué ficheros se verían afectados antes de ejecutar nada.

Soluciones

Solución al Ejercicio 1

ls envios-2026-08-??.csv          # (a) exactamente dos dígitos y termina en .csv
ls envios-2026-08-2[0-9].csv      # (b) días 20 a 29
ls [!e]*                          # (c) no empiezan por e → notas.txt
ls envios-2026-08-?[15].csv       # (d) segundo dígito 1 o 5: 01,05,11,15,21,25,31

En (a), el truco está en ??: al exigir exactamente dos caracteres antes de .csv, se excluye automáticamente envios-2026-08-03.csv.bak, porque ese nombre tiene más texto después. Un patrón envios*.csv habría fallado en el sentido contrario, y envios* habría incluido el .bak.

Solución al Ejercicio 2

El primero lista, sin más, envios-2026-08-01.csv envios-2026-08-02.csv. El segundo lista esos dos y además imprime tres errores: ls: no se puede acceder a 'envios-2026-08-03.csv': No existe el fichero o el directorio, y lo mismo para el 04 y el 05.

El globbing consulta el disco y devuelve solo lo que existe: por definición nunca puede informarte de una ausencia. La expansión de llaves genera los cinco nombres a ciegas y deja que ls se queje de los tres que no están. Por tanto, para detectar días que faltan en el histórico se usa la segunda forma: los errores son precisamente el resultado que buscas. Es un buen ejemplo de que «producir un error» puede ser el comportamiento deseado.

Solución al Ejercicio 3

shopt -s nullglob   # si no hay .log, el bucle no itera en lugar de hacerlo una vez
shopt -s failglob   # ALTERNATIVA: aborta con error si no hay coincidencias
shopt -u dotglob    # asegurarse de no arrastrar ficheros ocultos

# Comprobación sin riesgo, antes de tocar nada
cd ~/veloz-ops/logs
echo *.log          # ver la lista exacta que recibiría el comando
ls -lh *.log        # ver además tamaño y fecha de cada uno

nullglob es la elección correcta para un script de archivado rutinario: si un día no hay logs que archivar, lo esperable es que el script no haga nada, no que falle. failglob sería preferible en un script cuya ausencia de ficheros indicase una anomalía —por ejemplo, si veloz-api siempre debe generar logs y no hay ninguno—, porque entonces el silencio sería el peor resultado posible. Ambas resuelven el mismo problema con criterios opuestos, y elegir bien depende de si «no hay ficheros» es normal o alarmante en tu caso.

La comprobación con echo es el hábito clave: muestra exactamente la lista de argumentos que recibirá el comando real, con el mismo patrón, en el mismo directorio y con las mismas opciones de shell activas.

Conclusión

Ahora sabes que los comodines los expande el shell y no el comando, y has comprobado con echo que cuando rm o cp se ejecutan ya reciben la lista completa de nombres. Manejas *, ?, los conjuntos [...] con negación y rangos, y las clases POSIX robustas frente a los acentos. Distingues la expansión de llaves —que fabrica texto sin mirar el disco, y por eso sirve para crear y para detectar ausencias— del globbing, que solo selecciona lo existente. Sabes en qué se diferencian comodines y expresiones regulares, ajustas el comportamiento con shopt (nullglob, failglob, dotglob, nocaseglob, globstar) y conoces el comportamiento por defecto ante un patrón sin coincidencias, que es la causa silenciosa de tantos bucles rotos.

Con esto cierras las herramientas de manipulación de ficheros del módulo. Las búsquedas por criterios que un patrón de nombre no puede expresar —fecha, tamaño, permisos, profundidad— son territorio de find, en 05-01.

Queda una última pieza, y es puramente de productividad. En la lección 02-06 aprenderás a moverte por la terminal a la velocidad de un profesional: reutilizar comandos del historial, buscar hacia atrás con Ctrl-R, editar la línea sin flechas y automatizar tu día a día con alias. Es la lección que cierra el módulo y te deja listo para dar el salto de la terminal al script.

Curso de Programación en Bash

Módulo 1: Introducción a Bash

Módulo 2: Comandos Básicos de Bash

Módulo 3: Fundamentos de Scripting

Módulo 4: Scripting Intermedio

Módulo 5: Técnicas Avanzadas de Scripting

Módulo 6: Trabajando con Herramientas Externas

Módulo 7: Automatización y Programación

Módulo 8: Mejores Prácticas y Optimización

Módulo 9: Proyectos del Mundo Real

© Copyright 2026. Todos los derechos reservados