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
- Qué es el globbing y quién lo hace
- Los comodines básicos:
*,?y[...] - Clases de caracteres POSIX
- Expansión de llaves:
{} - Globbing frente a expresiones regulares
- Ajustar el comportamiento con
shopt - Qué ocurre cuando un patrón no casa con nada
- Peligros clásicos
- Casos prácticos sobre los datos de Veloz Envíos
- 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:
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
*.logen 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/*.logfunciona 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 quegrep "*.csv" ficherono hace lo que esperas yfind . -name "*.csv"sí: enfindqueremos que el patrón llegue sin expandir al programa.
- Los comodines básicos:
*, ? y [...]
*, ? 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 29Dos 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$HOMEborraría toda tu configuración. Se cambia condotglob, 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.
- 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úsculaFí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.
- 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-08Ninguno 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 delanteEl 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:
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.
- 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 ERROREn 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.
- Ajustar el comportamiento con
shopt
shoptshopt (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 opcionesglobstar 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.
- Qué ocurre cuando un patrón no casa con nada
Este es el comportamiento por defecto más contraintuitivo de Bash:
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:
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:
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.
- 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.
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.
- 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
echodelante 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 *.logsin comillas falla porque el shell lo expande antes de quefindlo 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
**singlobstar. 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
rmy*.
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:
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,31En (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 unonullglob 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
- ¿Qué es Bash?
- Configurando tu Entorno
- Navegación Básica en la Línea de Comandos
- Entendiendo el Shell
- Encontrar Ayuda: man, help y --help
Módulo 2: Comandos Básicos de Bash
- Operaciones con Archivos y Directorios
- Comandos de Procesamiento de Texto
- Permisos y Propiedad de Archivos
- Redirección y Tuberías
- Comodines y Expansión de Rutas
- Historial y Atajos de Teclado
Módulo 3: Fundamentos de Scripting
- Creando y Ejecutando un Script
- Variables y Constantes
- Operadores Básicos
- Sentencias Condicionales
- Argumentos y Entrada del Usuario
- Comillas, Expansión y Sustitución
Módulo 4: Scripting Intermedio
- Bucles en Bash
- Funciones en Bash
- Arrays y Arrays Asociativos
- Manipulación de Cadenas
- La Sentencia case y los Menús Interactivos
- Aritmética y Cálculos Numéricos
Módulo 5: Técnicas Avanzadas de Scripting
- Operaciones Avanzadas con Archivos
- Gestión de Procesos
- Manejo de Errores y Depuración
- Expresiones Regulares
- Entrada/Salida Avanzada: Descriptores y Here-Documents
- Scripts Modulares y Librerías Reutilizables
Módulo 6: Trabajando con Herramientas Externas
Módulo 7: Automatización y Programación
- Trabajos Cron
- Automatizando Tareas
- Scripts de Respaldo y Restauración
- Monitoreo y Registro
- Servicios y Temporizadores con systemd
- Automatización Remota con SSH
Módulo 8: Mejores Prácticas y Optimización
- Escribiendo Código Legible
- Optimizando Scripts en Bash
- Consideraciones de Seguridad
- Control de Versiones con Git
- Análisis Estático con ShellCheck y shfmt
- Pruebas Automatizadas con Bats
- Portabilidad: POSIX sh frente a Bashismos
