Ya tienes informe-diario.sh programado a las 06:30 y estado-servicio.sh cada 10 minutos. Esta lección no es cron otra vez: es la otra mitad del problema. Programar un script es fácil; lo difícil es que ese script esté escrito para ejecutarse sin nadie delante. Un script pensado para una persona en una terminal da por supuesto que alguien puede contestar una pregunta, ver un aviso en pantalla, pulsar Ctrl+C si se cuelga y relanzarlo si falla. A las 06:30 de la madrugada no hay nada de eso. Vamos a convertir «ejecutarse solo» en una lista de siete propiedades concretas y a implementarlas una a una hasta dejar informe-diario.sh verdaderamente desatendido.
Contenido
- Las siete propiedades de una tarea desatendida
- No interactiva: nada que preguntar
- Idempotente: ejecutarla dos veces no hace daño
- Exclusiva: una sola a la vez
- Observable: contar lo que ha hecho
- Acotada y segura ante fallos
- Configurable:
--dry-runy--force - Notificar, no heredar el entorno y documentar
- Aplicación:
informe-diario.shlisto para producción
- Las siete propiedades de una tarea desatendida
Cada propiedad responde a una pregunta que solo aparece cuando no hay nadie mirando:
| # | Propiedad | Pregunta que responde | Herramientas |
|---|---|---|---|
| 1 | No interactiva | ¿Y si pregunta algo y nadie contesta? | Opciones, configuración, [[ -t 0 ]] |
| 2 | Idempotente | ¿Y si se ejecuta dos veces? | mkdir -p, escritura atómica, marcas |
| 3 | Exclusiva | ¿Y si la anterior sigue viva? | flock |
| 4 | Observable | ¿Cómo sé mañana si funcionó? | Códigos de salida, log con marca de tiempo |
| 5 | Acotada | ¿Y si se cuelga para siempre? | timeout, reintentos limitados |
| 6 | Segura ante fallos | ¿Y si falla a la mitad? | set -euo pipefail, trap |
| 7 | Configurable | ¿Cómo la pruebo sin romper nada? | --dry-run, --force |
Ninguna es opcional: a un script automático al que le falte una, tarde o temprano le ocurrirá el incidente que esa propiedad evitaba.
- No interactiva: nada que preguntar
La regla es absoluta: un script automático no pregunta nada. Todo lo que un humano decidiría llega por una de tres vías: opciones de línea de comandos (03-05) para lo que cambia en cada ejecución, fichero de configuración (05-06) para lo estable de cada máquina, y variables de entorno para lo que inyecta quien lanza (cron, systemd, un contenedor). Y un read en automático no falla de forma limpia: se queda esperando. Si la entrada estándar está cerrada, devuelve error y con set -e el script muere con un mensaje incomprensible; si hay algo en ella, se lo come y sigue con un valor absurdo. La defensa activa es detectar que no hay terminal y negarse a preguntar:
confirma() {
local respuesta
[[ -t 0 ]] || { veloz_log_error "hace falta confirmar y no hay terminal; usa --force"; return 1; }
read -r -p "$1 [s/N] " respuesta
[[ ${respuesta,,} == s ]]
}[[ -t 0 ]] comprueba si el descriptor 0 está asociado a un terminal (05-05). Bajo cron no lo está, así que la función devuelve 1 con un mensaje claro en vez de colgarse. Compruébalo tú mismo: [[ -t 0 ]] && echo si || echo no responde si en tu terminal y no si lo ejecutas como echo | bash -c '…'. Su hermano [[ -t 1 ]] sirve para decidir si colorear la salida: con colores en un fichero de log acabas con \033[31m por todas partes.
- Idempotente: ejecutarla dos veces no hace daño
Idempotente significa que ejecutar la operación una vez o cinco deja el sistema en el mismo estado. Es la propiedad que hace seguro un reintento, y en automatización los reintentos son constantes: cron se solapa, alguien relanza a mano tras un fallo, el cambio de hora duplica una ejecución (07-01), una tarea muere a la mitad.
| No idempotente | Idempotente | Por qué |
|---|---|---|
mkdir /srv/veloz/informes |
mkdir -p /srv/veloz/informes |
-p no falla si ya existe |
echo "$linea" >> resumen.txt |
printf '%s\n' "$linea" > resumen.txt |
>> duplica al repetir |
ln -s origen destino |
ln -sfn origen destino |
-f reemplaza el enlace existente |
Cuatro patrones resuelven casi todo. (a) Operaciones ya idempotentes por diseño: mkdir -p, rm -f, ln -sfn, install -d, rsync; prefiérelas siempre. (b) Comprobar antes de actuar, devolviendo 0, no error —«ya estaba hecho» es éxito, y un script que devuelve error por eso generará una alerta falsa cada noche—. (c) Escritura atómica: temporal más mv, el patrón más importante de los cuatro:
[[ -f $destino ]] && { veloz_log_info "el informe de $fecha ya existe"; return 0; } # (b)
tmp=$(mktemp "${destino}.XXXXXX") || veloz_morir 1 "no puedo crear el temporal" # (c)
generar_informe > "$tmp"
mv -f "$tmp" "$destino" # renombrado atomico dentro del mismo sistema de ficherosEscribir directamente sobre $destino lo deja a medias si el script muere, y otro proceso puede leer basura. Con temporal más mv, el destino o es el antiguo completo o es el nuevo completo, nunca un híbrido, porque rename(2) es atómico. La condición es que el temporal esté en el mismo sistema de ficheros; por eso el mktemp usa el directorio del destino y no /tmp. (d) Marcas de trabajo hecho para tareas caras: un fichero testigo ([[ -e $marca ]] && exit 0) que se crea solo al terminar con éxito.
- Exclusiva: una sola a la vez
Ya viste flock en 05-02 y su uso como envoltorio en el crontab (flock -n /var/lock/veloz.lock comando). La pregunta ahora es dónde ponerlo, y hay dos respuestas: en el crontab, que es una línea y no toca el script; o dentro del propio script, que protege se lance como se lance —desde cron, a mano, desde un timer o desde otro script—. Para una tarea importante, la segunda es la correcta:
toma_candado() {
exec 9>/var/lock/veloz-informe.lock || veloz_morir 1 "no puedo abrir el candado"
flock -n 9 || { veloz_log_info "otra ejecucion en curso; salgo sin hacer nada"; exit 0; }
}Tres detalles que se escapan. exec 9> abre el descriptor 9 para toda la vida del script, y el candado se libera solo cuando el proceso termina: no hay que soltarlo ni en el trap. flock -n no espera (con flock -w 30 9 esperarías 30 segundos por si la anterior está acabando). Y aquí sale con exit 0, no con error, porque «ya hay una ejecución en marcha» es comportamiento normal, no una avería que merezca alerta. El fichero de candado va en /var/lock o /run/lock, nunca dentro del directorio de datos que la tarea manipula.
- Observable: contar lo que ha hecho
Si mañana no puedes responder «¿se ejecutó, cuánto tardó y qué hizo?» mirando un fichero, la tarea no es observable. Hacen falta tres cosas. Códigos de salida significativos, retomando la tabla de 05-03: 0 es todo bien —incluido «ya estaba hecho»—, 64 error de uso, 65 datos de entrada corruptos, 69 servicio no disponible, 75 fallo temporal y 78 error de configuración. Así quien llame decide sin leer el texto: 75 se reintenta con retroceso, 64 y 78 hay que arreglarlos a mano.
Mensajes con marca de tiempo y nivel, porque echo "empezando" no sirve en un log de tres meses:
veloz_log() { printf '%s [%s] %s\n' "$(date -Is)" "$1" "${*:2}" >&2; }
veloz_log INFO "procesados 1284 envios" # 2026-08-03T06:30:04+02:00 [INFO] procesados 1284 enviosdate -Is da la marca ISO-8601 con zona horaria, que ordena bien alfabéticamente y no es ambigua. El diseño completo del registro —niveles, umbrales, componente, rotación— es el tema de 07-04. Y un resumen final legible: la última línea debe permitir juzgar la ejecución de un vistazo (FIN ok: 1284 envios, 37 incidencias, 12s, salida $destino).
- Acotada y segura ante fallos
Una tarea que se cuelga es peor que una que falla: no avisa, ocupa el candado, bloquea las siguientes y acumula procesos durante días. Todo lo que hable con el exterior necesita un límite de tiempo, y en dos niveles que se complementan: timeout 30s curl … dentro del script para cortar la operación concreta con un código tratable, y timeout 300s /ruta/script.sh desde el crontab como red de seguridad. timeout devuelve 124 cuando corta, y tu script debe distinguirlo de un fallo normal. Los reintentos, además, van limitados y con retroceso, retomando 05-03:
reintenta() { # reintenta N comando...
local intentos="$1" espera=2 i; shift
for (( i = 1; i <= intentos; i++ )); do
"$@" && return 0
(( i < intentos )) && { sleep "$espera"; espera=$(( espera * 2 )); }
done
veloz_log ERROR "agotados los $intentos intentos de: $*"; return 75
}Tres cosas lo hacen correcto: hay un máximo de intentos (un while true reintentando eternamente es otra forma de colgarse), la espera se duplica para no machacar un servicio que ya sufre, y el último fallo devuelve 75 (EX_TEMPFAIL), que le dice al llamante «esto puede ser transitorio».
La cabecera del Módulo 5 sigue siendo la base —set -euo pipefail— y la limpieza garantizada con trap importa aquí el doble: nadie va a borrar a mano el temporal de 400 MB que quedó huérfano.
TRABAJO=$(mktemp -d "${TMPDIR:-/tmp}/informe.XXXXXX")
limpieza() {
local codigo=$?; rm -rf "$TRABAJO"
(( codigo == 0 )) && veloz_log INFO "FIN ok" || veloz_log ERROR "FIN con error ($codigo)"
exit "$codigo"
}
trap limpieza EXIT
# Error RECUPERABLE declarado explicitamente: el 'if !' neutraliza set -e para esa orden
if ! metricas=$(veloz_api_get /metricas); then
veloz_log WARN "sin metricas; el informe saldra sin la seccion de rendimiento"
metricas='{}'; (( ++AVISOS ))
fiLo que separa un script automático maduro de uno frágil es distinguir el error recuperable del fatal. Fatal: falta el CSV de envíos, no existe el directorio de salida, falta jq → registrar, salir con código y no continuar. Recuperable: una línea del CSV mal formada, la API no contesta a /metricas → registrar como WARN, contarlo, continuar y reflejarlo en el resumen. Como set -e aborta ante cualquier fallo, los recuperables hay que declararlos, y el if ! es la forma de hacerlo (05-03). Un script que aborta por no poder leer una métrica opcional es tan malo como uno que sigue adelante con el CSV corrupto: la decisión es tuya, orden por orden.
- Configurable:
--dry-run y --force
--dry-run y --forceUna tarea que borra, mueve o publica cosas debe poder ejecutarse en falso. --dry-run permite estrenar una tarea, verificar un cambio de configuración o entender qué haría hoy, sin consecuencias. La implementación limpia no es llenar el código de if, sino un prefijo:
SIMULAR=0; EJECUTA=() # array vacio: no antepone nada
(( SIMULAR )) && EJECUTA=(echo "[SIMULACION]")
"${EJECUTA[@]}" mv -f "$tmp" "$destino" # cada accion CON EFECTO lleva el prefijo
"${EJECUTA[@]}" rm -f "$antiguo"En modo normal EJECUTA está vacío y la expansión desaparece por completo, así que se ejecuta mv … tal cual. En simulación se expande a echo "[SIMULACION]" mv …, que imprime la orden ([SIMULACION] mv -f /tmp/informe.a3Kx9 /srv/veloz/informes/informe-2026-08-02.txt) en lugar de ejecutarla. Es el idioma de array de 04-03, y la regla es: solo las órdenes con efecto llevan el prefijo; leer, contar y calcular se hacen igual en los dos modos, para que la simulación sea realista. --force es el complemento: salta comprobaciones y confirmaciones, y es lo que da salida a la función confirma del apartado 2 —sin terminal y sin --force se niega; con --force sigue, nunca al revés—.
- Notificar, no heredar el entorno y documentar
Notificar. Nadie lee logs que no fallan. Para que un fallo llegue a un humano hay tres vías: correo (salida no vacía más MAILTO en cron, 07-01), logger hacia el syslog (logger -t veloz-informe -p user.err "…"), y un fichero de estado que otro sistema lee, que es el más versátil y encaja con 06-05:
jq -n --arg cuando "$(date -Is)" --argjson codigo "$codigo" --argjson avisos "$AVISOS" \
'{tarea:"informe-diario", cuando:$cuando, codigo:$codigo, avisos:$avisos}' > "$BASE/logs/estado-informe.json"Ese fichero convierte «la tarea funcionó» en un dato consultable: el vigilante de 07-04 puede avisar si la última ejecución correcta tiene más de 26 horas. Es la única forma de detectar una tarea que no se ejecutó, algo que un log jamás dirá porque no hay línea que escribir cuando nada ocurre. No heredar el entorno. Ya viste en 07-01 que cron da un PATH mínimo; la conclusión de diseño va más allá: un script automático no debe confiar en lo que hereda, venga de cron, de systemd o de un ssh remoto (07-06). Se protege en cuatro líneas al principio:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export LC_ALL=C # comparaciones y ordenaciones predecibles (06-03)
umask 027 # permisos consistentes de lo que cree (02-03)
veloz_requiere jq awk curl flock # fallar al principio, no a la mitad (05-06)Fijar el PATH en el script y no en el crontab tiene una ventaja: la garantía viaja con él y funciona igual desde un timer o desde SSH.
Documentar. Cuando la tarea falle serán las 3 de la mañana y quien mire no serás tú. El runbook mínimo son cuatro respuestas en la cabecera del script, y cuesta cinco minutos escribirlo:
# QUE HACE: agrega /srv/veloz/datos/envios.csv del dia anterior y publica el informe.
# CUANDO: todos los dias a las 06:30 (crontab de 'veloz' en srv-veloz-01).
# LOG: ~/veloz-ops/logs/informe.log | estado: logs/estado-informe.json
# SI FALLA: relanzar con --fecha AAAA-MM-DD es seguro (es idempotente).
# Codigo 65 = CSV corrupto, 69 = API caida, 64 = mala invocacion.
- Aplicación:
informe-diario.sh listo para producción
informe-diario.sh listo para producciónEl esqueleto de automatización completo; la lógica de awk y jq ya la tienes del Módulo 6:
#!/usr/bin/env bash
# (aqui va el bloque de runbook del apartado 8)
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export LC_ALL=C; umask 027
readonly BASE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
. "$BASE/lib/comun.sh"
[[ -r $BASE/etc/veloz-ops.conf ]] && . "$BASE/etc/veloz-ops.conf"
FECHA=$(date -d yesterday +%F); SIMULAR=0; FORZAR=0; EJECUTA=(); AVISOS=0
procesa_opciones() {
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--dry-run) SIMULAR=1 ;;
-f|--force) FORZAR=1 ;;
--fecha) FECHA="${2:?falta la fecha}"; shift ;;
*) veloz_morir 64 "opcion desconocida: $1" ;;
esac
shift
done
[[ $FECHA =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || veloz_morir 64 "fecha invalida: $FECHA"
(( SIMULAR )) && EJECUTA=(echo "[SIMULACION]")
}
main() {
procesa_opciones "$@"
veloz_requiere awk jq curl
exec 9>/var/lock/veloz-informe.lock
flock -n 9 || { veloz_log_info "otra ejecucion en curso"; exit 0; }
TRABAJO=$(mktemp -d "${TMPDIR:-/tmp}/informe.XXXXXX"); trap 'rm -rf "$TRABAJO"' EXIT
local destino="$INFORMES_DIR/informe-$FECHA.txt"
[[ -f $destino && $FORZAR -eq 0 ]] && { veloz_log_info "informe de $FECHA ya hecho"; return 0; }
veloz_log_info "INICIO informe de $FECHA (simular=$SIMULAR)"
[[ -r $ENVIOS_CSV ]] || veloz_morir 65 "no puedo leer $ENVIOS_CSV"
if ! METRICAS=$(timeout 20s veloz_api_get /metricas); then
veloz_log_error "sin metricas de la API; sigo sin esa seccion"
METRICAS='{}'; (( ++AVISOS ))
fi
genera_informe "$FECHA" > "$TRABAJO/informe.txt" # awk, del Modulo 6
"${EJECUTA[@]}" mkdir -p "$INFORMES_DIR"
"${EJECUTA[@]}" mv -f "$TRABAJO/informe.txt" "$destino"
publica_resumen_json "$FECHA" "$METRICAS" "$AVISOS" # jq -n --arg, del Modulo 6
veloz_log_info "FIN ok: informe de $FECHA en $destino ($AVISOS avisos)"
}
main "$@"Recorre el listado buscando las siete: no interactiva (todo por opciones y configuración, ningún read), idempotente (mkdir -p, la comprobación de $destino que devuelve 0, temporal más mv atómico), exclusiva (candado con descriptor 9), observable (INICIO y FIN, códigos 64/65, resumen JSON), acotada (timeout 20s en la API), segura ante fallos (set -euo pipefail, trap, CSV ilegible fatal frente a métricas ausentes recuperables) y configurable (--dry-run, --force, --fecha).
La línea de crontab final es más corta que la de 07-01, porque el script ya se protege solo, y ahora es posible probar antes de instalar con ~/veloz-ops/bin/informe-diario.sh --dry-run --fecha 2026-08-02:
30 6 * * * /usr/bin/timeout 900s /home/veloz/veloz-ops/bin/informe-diario.sh >> /home/veloz/veloz-ops/logs/informe.log 2>&1Errores Comunes y Consejos
- Dejar un
read«solo por si acaso». En automático cuelga el proceso o devuelve basura. Todo por opciones, configuración o entorno. - Confundir «ya estaba hecho» con error. Devolver código distinto de 0 genera una alerta falsa cada noche y la gente deja de mirarlas.
- Escribir directamente sobre el fichero final, o poner el temporal en
/tmp. Lo primero deja el destino corrupto si falla a la mitad; lo segundo hace que elmvdeje de ser atómico, porque copia y borra entre discos distintos. flocksin-nen una tarea periódica. Las ejecuciones se acumulan esperando en lugar de descartarse.- Reintentar sin límite ni retroceso, o llamar sin
timeout. Las dos son formas de colgarse eternamente, y la primera además machaca al servicio que ya está caído. --dry-runque no simula del todo, o colores en el log. Si una orden con efecto se cuela sin el prefijo la simulación miente; y sin decidir el color con[[ -t 1 ]]acabarás con secuencias de escape en un fichero de tres meses.- Consejo: antes de programar una tarea nueva, ejecútala tres veces seguidas a mano. Si la segunda y la tercera no son inofensivas, todavía no es idempotente y no está lista para cron.
Ejercicios
Ejercicio 1. Este fragmento pretende publicar el resumen del día. Señala los tres problemas que impiden ejecutarlo desatendido y reescríbelo.
read -p "Fecha a procesar: " fecha
mkdir /srv/veloz/informes/$fecha
curl http://localhost:8080/metricas > /srv/veloz/informes/$fecha/metricas.jsonEjercicio 2. Escribe una función veloz_escribe_atomico para lib/comun.sh que reciba la ruta de destino, lea el contenido de la entrada estándar y lo publique de forma atómica, respetando el modo simulación mediante la variable SIMULAR.
Soluciones
Solución 1. Los tres problemas: (a) el read cuelga el script sin terminal; (b) mkdir sin -p falla en la segunda ejecución y curl sin -sSf ni tiempos de espera puede colgarse o guardar una página de error como si fuera JSON; (c) se escribe directamente en el destino, así que un fallo a la mitad deja un JSON truncado. Además, la variable sin comillas rompería con cualquier valor con espacios (03-06).
fecha="${1:-$(date -d yesterday +%F)}"
[[ $fecha =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || veloz_morir 64 "fecha invalida: $fecha"
destino="/srv/veloz/informes/$fecha"; mkdir -p "$destino"
tmp=$(mktemp "$destino/metricas.XXXXXX") || veloz_morir 74 "sin temporal"
trap 'rm -f "$tmp"' EXIT
timeout 30s curl -sSf --connect-timeout 5 --max-time 25 http://localhost:8080/metricas > "$tmp" ||
{ veloz_log_error "sin metricas de $fecha"; exit 69; }
mv -f "$tmp" "$destino/metricas.json"La fecha llega ahora como argumento con un valor por defecto sensato (${1:-…} de 03-06), se valida con =~ (05-04), y el trap garantiza que no queden temporales aunque el curl falle.
Solución 2.
# veloz_escribe_atomico — publica stdin en un fichero sin dejarlo nunca a medias.
# Uso: generar_algo | veloz_escribe_atomico /ruta/destino
veloz_escribe_atomico() {
local destino="${1:?falta el destino}" dir tmp
dir=$(dirname -- "$destino")
[[ -d $dir ]] || mkdir -p "$dir" || { veloz_log_error "no puedo crear $dir"; return 74; }
if (( ${SIMULAR:-0} )); then
veloz_log_info "[SIMULACION] escribiria $(wc -c) bytes en $destino"; return 0
fi
tmp=$(mktemp "$destino.XXXXXX") || { veloz_log_error "sin temporal junto a $destino"; return 74; }
cat > "$tmp" && mv -f "$tmp" "$destino" && return 0
rm -f "$tmp"; veloz_log_error "fallo al publicar $destino"; return 74
}El mktemp se crea junto al destino ("$destino.XXXXXX") y no en /tmp, que es la condición para que el mv sea un renombrado atómico y no una copia entre discos. En modo simulación consume igualmente la entrada con wc -c para no romper la tubería que la alimenta: si no leyeras stdin, el proceso de la izquierda recibiría un SIGPIPE. Y ante cualquier fallo se borra el temporal, dejando el destino anterior intacto.
Conclusión
Un script listo para ejecutarse solo cumple siete propiedades. Es no interactiva: cero read, todo por opciones, configuración o entorno, y [[ -t 0 ]] para negarse a preguntar cuando no hay nadie. Es idempotente: mkdir -p, comprobar antes de actuar devolviendo 0 cuando ya estaba hecho, escribir a temporal y publicar con mv atómico, y marcas para lo caro. Es exclusiva: flock -n como envoltorio en el crontab y, mejor, con exec 9> dentro del script para que proteja se lance como se lance. Es observable: códigos que distinguen tipos de fallo, líneas con date -Is y nivel, y un resumen final que se entiende de un vistazo. Es acotada: timeout en dos niveles sobre todo lo que hable con el exterior, y reintentos con máximo y retroceso. Es segura ante fallos: set -euo pipefail, trap de limpieza y una decisión consciente, orden por orden, sobre qué error es fatal y cuál recuperable. Y es configurable: --dry-run con el prefijo "${EJECUTA[@]}" para probarla sin consecuencias, y --force para saltarse las confirmaciones que en automático no puede contestar nadie. Encima de eso, dos cosas que no son código: no heredar el entorno —fija PATH, LC_ALL y umask, y comprueba las herramientas al empezar— y escribir el runbook mínimo en la cabecera.
Con la técnica de tareas desatendidas en la mano, en 07-03 la aplicamos a la operación que más se agradece haber automatizado y peor se lleva improvisar: el respaldo. Verás qué respaldar y qué no, la regla 3-2-1, la diferencia entre copia completa, incremental y diferencial, rsync --link-dest para copias incrementales que se restauran como completas, políticas de retención, verificación con sha256sum —porque un respaldo no verificado no es un respaldo— y, sobre todo, la parte que casi nadie prueba hasta que la necesita: la restauración. Nace ~/veloz-ops/bin/respaldo.sh.
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
