A lo largo del módulo has escrito "$RUTA_CSV", "$@", ${fecha} y ${VELOZ_UMBRAL:-50} siguiendo instrucciones, sin una explicación completa de por qué. Esta lección paga esa deuda, y no es un detalle menor: la inmensa mayoría de los bugs de scripting en Bash —los que aparecen tres meses después, con un fichero que tenía un espacio en el nombre o una variable que llegó vacía— son problemas de comillas y expansión. Entender el orden exacto en que Bash procesa una línea es lo que separa un script que funciona casi siempre de uno que funciona siempre.
Contenido
- El orden de las expansiones
- Comillas dobles, simples y sin comillas
- Entrecomillar siempre las variables, y el escape con
\ - Expansión de parámetros: valores por defecto y validación
- Sustitución de comandos y expansión aritmética
IFSy la división en palabras- El desastre de
for f in $(ls) printffrente aecho- Repaso final de
informe-diario.sh
- El orden de las expansiones
Antes de ejecutar cualquier comando, Bash transforma la línea que has escrito siguiendo siempre la misma secuencia, y conocerla explica casi todo lo demás:
flowchart LR
A["Línea<br/>escrita"] --> B["1 Llaves<br/>2 Tilde"]
B --> D["3 Parámetros<br/>$var"]
D --> E["4 Comandos<br/>5 Aritmética"]
E --> G["6 División<br/>en palabras"]
G --> H["7 Globbing<br/>*.csv"] --> I["Comando<br/>ejecutado"]
Con ejemplos concretos: las llaves convierten envios-{01,02}.csv en dos nombres; la tilde convierte ~/veloz-ops en /home/joan/veloz-ops; los parámetros convierten $RUTA_CSV en la ruta del CSV; la sustitución de comandos convierte $(date +%F) en 2026-08-03; la aritmética convierte $(( 3 * 4 )) en 12; la división en palabras parte el resultado en argumentos; y el globbing convierte *.csv en los ficheros que existen.
La clave de toda la lección está en el orden de los pasos 3 y 6. Bash primero sustituye la variable por su valor, y después parte el resultado en palabras por los espacios. Es decir: si una variable contiene espacios, esos espacios se convierten en separadores de argumentos después de que la variable haya desaparecido.
fichero="envios julio.csv"
wc -l $fichero # Bash ejecuta: wc -l envios julio.csv → DOS ficheros, error
wc -l "$fichero" # Bash ejecuta: wc -l "envios julio.csv" → UNO, correctoLas comillas dobles no impiden que la variable se expanda; impiden el paso 6, la división en palabras. Ese es todo su secreto.
- Comillas dobles, simples y sin comillas
Bash ofrece tres niveles de protección, y esta tabla resume qué ocurre en cada uno:
| Se expande | Sin comillas | "Dobles" |
'Simples' |
|---|---|---|---|
Variables $var, $(cmd), $(( )) |
Sí | Sí | No |
Escape \ |
Sí | Parcial | No |
| División en palabras | Sí | No | No |
Globbing * |
Sí | No | No |
Tilde ~ y llaves {a,b} |
Sí | No | No |
Las filas en negrita son las que importan: las comillas dobles expanden variables pero bloquean la división en palabras y el globbing, que es justo lo que quieres el 95% de las veces.
ciudad="San Sebastián"; patron="*.csv"
echo $ciudad # San Sebastián (dos argumentos que echo une con un espacio)
echo "$ciudad" # San Sebastián (un solo argumento)
echo '$ciudad' # $ciudad (literal, sin expandir)
ls $patron # lista los ficheros .csv reales (globbing activo)
ls "$patron" # busca un fichero llamado literalmente *.csvCon echo la diferencia entre las dos primeras parece cosmética, pero con mkdir o rm es catastrófica: mkdir $ciudad crearía dos directorios, San y Sebastián. Las comillas simples, por su parte, son absolutas: nada se expande dentro, ni siquiera la barra invertida. Son la elección correcta para expresiones regulares (05-04), patrones de sed (06-02) y cualquier texto que deba llegar literal a otro programa:
grep '^2026-08-03 .*\[ERROR\]' /var/log/veloz/app.log # regex literal
awk -F, '{ print $3 }' /srv/veloz/datos/envios.csv # $3 es de awk, no de BashSi esos patrones fueran con comillas dobles, Bash intentaría expandir $3 como una variable —que estaría vacía— y awk recibiría { print }: un fallo silencioso perfecto. Como las comillas simples no admiten escapes, para incluir una comilla simple dentro hay que cerrarlas, escapar y reabrir: 'no'\''funciona' produce no'funciona. Es feo, pero es la única forma.
- Entrecomillar siempre las variables, y el escape con
\
\La norma profesional es simple y no tiene apenas excepciones: entrecomilla toda expansión de variable, siempre. Estos son los tres desastres que evita. Rutas con espacios: wc -l $fichero se parte en varios argumentos. Con un comando destructivo, rm -rf $directorio sobre directorio="/srv/veloz datos" borraría /srv/veloz y datos.
Variables vacías: al expandirse sin comillas, una variable vacía desaparece por completo en lugar de convertirse en una cadena vacía:
ciudad=""; patron="ERROR*"
[ $ciudad = "Bilbao" ] # bash: [: =: se esperaba una expresión unaria
[ "$ciudad" = "Bilbao" ] # funciona: compara "" con "Bilbao"
grep $patron app.log # si existe un fichero ERRORES.txt, el patrón se vuelve él
grep "$patron" app.log # el patrón llega intacto a grepSin comillas, test recibe [ = Bilbao ] y no entiende nada, porque el primer operando se ha esfumado antes de que lo viera. Recuerda de 03-03 que [[ ]] es inmune a esto; [ ] no.
Globbing accidental: es el caso de las dos últimas líneas, si una variable contiene * o ?, sin comillas Bash intenta expandirla contra los ficheros del directorio. Es especialmente traicionero porque el script funciona hasta que alguien crea un fichero con el nombre equivocado en el directorio de trabajo.
Las únicas excepciones razonables son el interior de [[ ]] a la izquierda del operador, el interior de (( )), y los casos en que quieres deliberadamente la división en palabras. Ante la duda, entrecomilla: poner comillas de más nunca ha roto un script; ponerlas de menos, todos los días.
Escape con la barra invertida
La barra invertida \ protege un único carácter del siguiente paso de procesamiento:
Por ejemplo, echo "El coste es \$50" imprime El coste es $50, y en echo Fichero:\ envios\ julio.csv los espacios escapados no separan palabras. Dentro de comillas dobles, la barra invertida solo tiene efecto sobre cuatro caracteres: $, `, " y \ (y el salto de línea). Ante cualquier otro se imprime literalmente, de modo que echo "ruta\nueva" produce ruta\nueva. Ese detalle sorprende a quien viene de otros lenguajes: echo "\n" no imprime un salto de línea en Bash por defecto. Para eso hace falta echo -e o, mejor, printf (apartado 8).
Al final de una línea, la barra invertida escapa el propio salto de línea y permite partir comandos largos, que es lo que has visto en todos los scripts del módulo. Cuidado: no puede haber ni un espacio después de la \. Si lo hay, la barra escapa ese espacio en lugar del salto y el comando se rompe con un error desconcertante.
- Expansión de parámetros: valores por defecto y validación
La familia ${var...} va mucho más allá de leer un valor; estas cinco formas son las de uso diario:
| Forma | Qué hace |
|---|---|
${var:-def} |
Devuelve def si var está vacía o no existe. No modifica var |
${var:=def} |
Devuelve def y además asigna def a var |
${var:?msg} |
Si var está vacía, escribe msg en stderr y aborta el script |
${var:+val} |
Devuelve val solo si var tiene valor (el caso contrario) |
${#var} |
Devuelve la longitud de la cadena |
ciudad=""
echo "${ciudad:-todas}" # todas (ciudad sigue vacía)
echo "${ciudad:=todas}" # todas (y ahora ciudad vale "todas")
echo "${#ciudad}" # 5 (longitud de "todas")
echo "${RUTA_CSV:?falta la ruta del CSV}" # aborta: bash: RUTA_CSV: falta la ruta...${var:?} es la joya de la familia para scripts de producción: convierte una variable obligatoria sin valor en una muerte inmediata con mensaje claro, en lugar de dejar que el script siga adelante con rutas vacías. Compara los dos escenarios: sin él, rm -rf "$DIR_TEMPORAL/"* con la variable vacía se convierte en rm -rf /*. Con ${DIR_TEMPORAL:?}, el script muere antes de llegar ahí.
${var:+val} parece rebuscada, pero resuelve muy bien la construcción de opciones condicionales: filtro="${ciudad:+--ciudad $ciudad}" produce --ciudad Bilbao si hay ciudad y una cadena vacía si no. Un matiz sobre los dos puntos: ${var-def} (sin :) solo aplica el valor por defecto si la variable no existe, mientras que ${var:-def} lo aplica también si existe pero está vacía; en la práctica casi siempre quieres la versión con :.
Las operaciones de manipulación de cadenas de la misma familia —${var#patron}, ${var/a/b}, ${var^^}, ${var:0:5}— son la lección 04-04.
- Sustitución de comandos y expansión aritmética
Ya usas $(comando) desde 03-02; aquí van los tres matices que faltaban. Entrecomíllala siempre, por el mismo motivo que las variables: su resultado sufre división en palabras. Y se anida sin escapes, que es su gran ventaja sobre las comillas invertidas:
archivos=$(ls /srv/veloz/datos)
echo "$archivos" # respeta los saltos de línea; sin comillas los vuelve espacios
echo "Log más reciente: $(basename "$(ls -t /var/log/veloz/*.log | head -1)")"Fíjate en que las comillas dobles internas de la última línea funcionan perfectamente: dentro de $( ) empieza un contexto nuevo, así que puedes volver a usar comillas dobles sin escaparlas. Con `...` habría que escapar cada nivel, y con dos niveles ya resulta ilegible.
La expansión aritmética $(( )) no necesita comillas internas pero sí externas si el resultado se usa como argumento. Dentro de $(( )) no hay división en palabras ni globbing —solo aritmética—, así que ahí las variables van sin $ y sin comillas sin ningún riesgo: es la única zona de Bash donde puedes relajarte.
IFS y la división en palabras
IFS y la división en palabrasIFS (Internal Field Separator) es la variable que le dice a Bash por qué caracteres partir en palabras en el paso 6. Su valor por defecto son tres: espacio, tabulador y salto de línea (compruébalo con printf '%q\n' "$IFS"). Cambiarlo permite dividir por otro carácter, lo cual es muy útil con datos separados por comas:
linea="E-8821,2026-08-03,Bilbao,mgarcia,entregado,34.90"
IFS=',' read -r id fecha ciudad repartidor estado importe <<< "$linea"
echo "El envío $id de $repartidor en $ciudad está $estado"
# → El envío E-8821 de mgarcia en Bilbao está entregadoEse IFS=',' read es el patrón canónico para trocear una línea de CSV, y tiene un detalle importante: al poner la asignación delante del comando, IFS solo cambia durante esa invocación (lección 03-02) y se restaura sola después. Ese es el uso seguro. Modificar IFS de forma global —una línea IFS=',' suelta en mitad del script— es peligroso, porque afecta a todo lo que venga después y hay que restaurarlo a mano con IFS=$' \t\n'. Si olvidas restaurarlo, comandos posteriores que dependan de la división por espacios se comportarán de forma inexplicable. La práctica correcta es guardar el valor original (IFS_ORIGINAL=$IFS) y devolverlo, o mejor aún, usar la forma de una sola invocación.
- El desastre de
for f in $(ls)
for f in $(ls)Este antipatrón merece su propio apartado porque es el error más repetido en scripts de principiante, y reúne casi todos los conceptos de la lección. Nunca escribas for f in $(ls /srv/veloz/datos). Qué falla, punto por punto:
- Los nombres con espacios se parten.
envios julio.csvproduce dos vueltas del bucle, conenviosyjulio.csv. Y los nombres con salto de línea (raros pero legales) rompen cualquier suposición. - El globbing se aplica al resultado. Si un fichero se llama
*,$(ls)lo devuelve y Bash lo expande contra el directorio. lsda formatos distintos según si su salida va a un terminal o a una tubería, y según opciones y alias del usuario. Además es un proceso de más, innecesario por completo.
La solución correcta es usar globbing directamente, que Bash gestiona sin dividir en palabras:
Cada $f es un nombre completo aunque contenga espacios, porque el globbing produce una lista de palabras ya separadas, no una cadena que haya que trocear. Recuerda de 02-05 que si el patrón no casa con nada, sin nullglob el bucle da una vuelta con el patrón literal; de ahí la guarda [[ -f "$f" ]].
La misma lógica desaconseja for linea in $(cat fichero): para recorrer líneas se usa while IFS= read -r linea; do ... done < fichero, que es la lección 04-01. Ese IFS= vacío al principio, por cierto, evita que read recorte los espacios del inicio y el final de cada línea; junto con -r, forma el idioma más seguro para leer texto en Bash.
printf frente a echo
printf frente a echoecho es cómodo pero no es predecible: su comportamiento con opciones y barras invertidas varía entre shells, entre versiones e incluso según la opción xpg_echo. ¿echo "-n" imprime -n o lo interpreta como opción? ¿echo "a\tb" produce un tabulador o el texto literal? Depende del shell. printf es POSIX, uniforme en todas partes, y además da control sobre el formato:
| Formato | Significado |
|---|---|
%s / %d / %.2f |
Cadena / entero / decimal con dos cifras |
%-20s |
Cadena alineada a la izquierda en 20 caracteres |
%% / \n |
Porcentaje literal / salto de línea (siempre interpretado) |
printf '%-12s %6s %8s\n' "CIUDAD" "ENVIOS" "IMPORTE"
printf '%-12s %6d %8.2f\n' "Bilbao" 148 3241.75 # → Bilbao 148 3241.75
printf '%-12s %6d %8.2f\n' "Valencia" 981 21470.30 # → Valencia 981 21470.30Dos características muy útiles. printf reutiliza el formato hasta agotar los argumentos, así que printf '%s\n' "$@" imprime cada argumento en su propia línea. Y %q escapa la salida para que sea reutilizable por el shell, lo que la convierte en la mejor herramienta para depurar valores con espacios: printf 'valor: %q\n' "$ciudad" imprime valor: San\ Sebastián.
Regla práctica: echo para mensajes simples e interactivos, printf en scripts serios, y siempre printf cuando el formato importe o el contenido pueda empezar por guion.
- Repaso final de
informe-diario.sh
informe-diario.shCerramos el módulo revisando el script con todo lo aprendido. Estas son las correcciones que aplica la versión definitiva:
#!/usr/bin/env bash
# informe-diario.sh - Resumen diario de Veloz Envíos [--fecha F] [--ciudad C] [-v]
readonly RUTA_APP_LOG="${VELOZ_APP_LOG:?falta definir la ruta de app.log}"
readonly RUTA_CSV="${VELOZ_CSV:?falta definir la ruta de envios.csv}"
readonly DIR_INFORMES="${VELOZ_LOGS:-$HOME/veloz-ops/logs}"
readonly UMBRAL_ERRORES="${VELOZ_UMBRAL:-50}"
uso() { printf 'Uso: %s [-f AAAA-MM-DD] [-c CIUDAD] [-v] [-h]\n' "$(basename "$0")"; }
fecha="$(date +%F)"
ciudad="${VELOZ_CIUDAD:-todas}"
verboso="no"
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--fecha) fecha="${2:?--fecha requiere un valor}"; shift 2 ;;
-c|--ciudad) ciudad="${2:?--ciudad requiere un valor}"; shift 2 ;;
-v|--verboso) verboso="si"; shift ;;
-h|--help) uso; exit 0 ;;
*) printf 'ERROR: opción desconocida %q\n' "$1" >&2; uso >&2; exit 2 ;;
esac
done
[[ -r "$RUTA_APP_LOG" && -s "$RUTA_CSV" ]] \
|| { printf 'ERROR: no puedo leer los datos de origen\n' >&2; exit 3; }
mkdir -p "$DIR_INFORMES" || exit 5
total_errores="$(grep -c "^$fecha .*ERROR" "$RUTA_APP_LOG")"
printf '%-18s %s\n' "INFORME VELOZ" "$fecha"
printf '%-18s %s (umbral %s)\n' "Errores del día:" "$total_errores" "$UMBRAL_ERRORES"
[[ "$verboso" == "si" ]] && grep "^$fecha .*ERROR" "$RUTA_APP_LOG" | tail -5
lineas="$(grep ",$fecha," "$RUTA_CSV")"
[[ "$ciudad" != "todas" ]] && lineas="$(printf '%s\n' "$lineas" | grep ",$ciudad,")"
printf '%s\n' "$lineas" | cut -d, -f5 | sort | uniq -c | sort -rn
exit 0Los cambios respecto a la versión de 03-05:
${VELOZ_APP_LOG:?...}en las rutas obligatorias: si la configuración no las define, el script muere al instante con un mensaje claro en lugar de trabajar sobre rutas vacías. Y${VELOZ_LOGS:-$HOME/veloz-ops/logs}en las opcionales, con valor por defecto sensato.${2:?--fecha requiere un valor}dentro delcase, que sustituye a la comprobación manual de 03-05 en una sola expresión.- Comillas en absolutamente todas las expansiones, incluidas
"$(date +%F)"y"$(grep -c ...)". printfen lugar deechoen toda la salida, con%qen los mensajes de error para que una opción con caracteres raros se vea tal cual llegó. En particular,printf '%s\n' "$lineas": si una línea del CSV empezara por-no contuviera barras invertidas,echopodría interpretarla;printf '%s\n'nunca.
Ese último punto ilustra la mentalidad de la lección: no se trata de que el script falle hoy, sino de que no pueda fallar el día que aparezca un dato inesperado. Un printf en lugar de un echo cuesta cuatro caracteres y elimina una clase entera de bugs.
Errores Comunes y Consejos
- No entrecomillar una variable. Rutas con espacios que se parten, variables vacías que desaparecen, globbing accidental. Es el error número uno.
- Usar comillas dobles en expresiones regulares o de
awk."$3"se expande a nada; usa comillas simples. Y no esperes queecho "\n"imprima un salto de línea: usaprintf '\n'. - Dejar un espacio después de la
\de continuación de línea. El comando se rompe y el error no lo indica. for f in $(ls). Usa globbing directo:for f in dir/*.- Modificar
IFSsin restaurarlo. Prefiere la formaIFS=',' read ..., que solo afecta a esa invocación. - Confundir
${var:-x}con${var:=x}. El primero no toca la variable; el segundo la asigna. Y no anides comillas invertidas: usa$( ), que anida sin escapes.
Ejercicios
Ejercicio 1 — Predice la salida. Con ciudad="San Sebastián" y n=3, di exactamente qué imprime cada línea y por qué: (a) echo $ciudad; (b) echo "$ciudad"; (c) echo '$ciudad tiene $n letras'; (d) echo "$ciudad tiene ${#ciudad} letras"; (e) echo "Total: $(( n * 2 ))€".
Ejercicio 2 — Blindar un script peligroso. Este fragmento tiene seis problemas de comillas y expansión; encuéntralos y reescríbelo.
DIR_BACKUP=$1
FECHA=`date +%F`
for f in $(ls /srv/veloz/datos); do cp $f $DIR_BACKUP/$f.$FECHA; done
echo "Copiados `ls $DIR_BACKUP | wc -l` ficheros"
rm -rf $DIR_BACKUP/tmp/*Ejercicio 3 — Informe alineado con printf. Lee envios.csv y produce una tabla con la ciudad alineada a la izquierda en 12 caracteres, el número de envíos a la derecha en 6, y el porcentaje sobre el total con una cifra decimal. Usa printf y expansión aritmética.
Soluciones
Solución al Ejercicio 1
| Línea | Salida | Motivo |
|---|---|---|
| (a) y (b) | San Sebastián |
En (a) se parte en dos argumentos, pero echo los une con un espacio; en (b) es un solo argumento |
| (c) | $ciudad tiene $n letras |
Comillas simples: nada se expande |
| (d) | San Sebastián tiene 13 letras |
${#ciudad} cuenta 13 caracteres, espacio incluido |
| (e) | Total: 6€ |
La aritmética se expande dentro de las comillas dobles |
El caso (a) es la trampa didáctica: con echo el resultado es idéntico al de (b), y por eso mucha gente concluye que las comillas son opcionales. Cambia echo por mkdir y (a) creará dos directorios.
Solución al Ejercicio 2
Los seis problemas: (1) $1 sin comillas ni validación; (2) comillas invertidas en lugar de $( ), dos veces; (3) for f in $(ls ...); (4) todas las variables de cp sin comillas; (5) $f contiene solo el nombre, no la ruta, así que el cp falla salvo que estés en ese directorio; (6) rm -rf $DIR_BACKUP/tmp/* sin comillas ni validación es una bomba, porque si $DIR_BACKUP está vacío se convierte en rm -rf /tmp/*.
readonly DIR_BACKUP="${1:?Uso: $(basename "$0") DIRECTORIO_DESTINO}"
readonly FECHA="$(date +%F)"
[[ -d "$DIR_BACKUP" ]] || { printf 'ERROR: %q no es directorio\n' "$DIR_BACKUP" >&2; exit 2; }
for f in /srv/veloz/datos/*; do
[[ -f "$f" ]] || continue
cp -a "$f" "$DIR_BACKUP/$(basename "$f").$FECHA"
done
printf 'Copiados %d ficheros\n' "$(find "$DIR_BACKUP" -maxdepth 1 -type f | wc -l)"
rm -rf "${DIR_BACKUP:?}/tmp/"*La última línea merece atención especial: "${DIR_BACKUP:?}" es el idioma defensivo estándar antes de cualquier rm -rf, porque si la variable estuviera vacía el script aborta con un error en lugar de ejecutar un borrado catastrófico en la raíz. Cuesta cinco caracteres y ha salvado muchos servidores.
Solución al Ejercicio 3. Cuidado con una trampa: Bash no hace decimales (03-02), así que la parte entera y la decimal hay que calcularlas por separado.
total=$(tail -n +2 "$RUTA_CSV" | wc -l)
printf '%-12s %6s %8s\n' "CIUDAD" "ENVIOS" "PORCENT."
tail -n +2 "$RUTA_CSV" | cut -d, -f3 | sort | uniq -c | sort -rn \
| while read -r n ciudad; do
printf '%-12s %6d %6d.%d%%\n' "$ciudad" "$n" \
"$(( n * 100 / total ))" "$(( n * 1000 / total % 10 ))"; doneLa salida alinea las columnas: Valencia, 981, 78.6%; Bilbao, 148, 11.8%. Fíjate en tres cosas. El %% del formato imprime un porcentaje literal. El while read -r lee dos campos por línea, aprovechando que IFS divide por espacios exactamente donde uniq -c los deja. Y la aritmética entera obliga a esa gimnasia de multiplicar por 1000 y sacar el módulo; en 06-01 verás que awk hace lo mismo con printf "%.1f%%" y sin acrobacias.
Conclusión
Has cerrado el hueco que quedaba. Sabes en qué orden expande Bash una línea, y que la división en palabras ocurre después de sustituir las variables —el hecho del que se derivan casi todos los bugs de comillas—. Distingues con precisión qué expande cada tipo de comilla; entrecomillas toda variable por norma, sabiendo qué se rompe si no; escapas con \ sin caer en el espacio después de la continuación de línea; manejas ${var:-def}, ${var:=def}, ${var:?msg}, ${var:+val} y ${#var}; anidas $( ) sin escapes; entiendes IFS y lo cambias solo por invocación; has desterrado for f in $(ls) a favor del globbing directo; y usas printf cuando la salida debe ser predecible.
Y con eso termina el Módulo 3. Repasa el camino: empezaste guardando en un fichero las tuberías que tecleabas cada mañana, y informe-diario.sh es hoy un comando de ~/veloz-ops/bin con constantes, validación de entorno, códigos de salida documentados, opciones --fecha y --ciudad, --help, configuración por variables de entorno y expansiones blindadas. Ha crecido en seis lecciones sin que ninguna versión rompiera la anterior, que es exactamente como crece el software real. Sus límites, eso sí, se ven a simple vista: el script repite la misma tubería con pequeñas variaciones, si quisieras el informe de cada ciudad tendrías que invocarlo cuatro veces a mano, y no hay forma de reutilizar en otro script la lógica de validación que tanto te ha costado escribir.
En el Módulo 4 llegan las herramientas que resuelven eso. Los bucles (04-01) recorrerán ciudades, fechas y ficheros; las funciones (04-02) empaquetarán la validación y el formato para reutilizarlos; los arrays (04-03) guardarán listas de repartidores y contadores por ciudad; la manipulación de cadenas (04-04) completará la familia ${var...} que has empezado hoy; case (04-05) recibirá el tratamiento formal que aquí solo has usado de pasada; y la aritmética (04-06) resolverá de una vez esos porcentajes con decimales. informe-diario.sh dejará de ser un guion lineal para convertirse en un programa con estructura.
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
