# Herramientas Avanzadas de Procesamiento y Shell Scripting ## Guía de Laboratorio (4 horas) ### Introducción Este laboratorio está diseñado para proporcionar una experiencia práctica en el uso avanzado de herramientas de procesamiento de texto y shell scripting en entornos Unix/Linux. A lo largo de la sesión, los estudiantes progresarán desde el uso de filtros básicos hasta la creación de scripts complejos que integren múltiples herramientas y técnicas avanzadas. ### Objetivos de Aprendizaje Al completar este laboratorio, los estudiantes serán capaces de: 1. Utilizar eficientemente las herramientas de procesamiento de texto de Unix 2. Crear scripts modulares y mantenibles en shell 3. Implementar soluciones para monitorización y análisis de sistemas 4. Aplicar buenas prácticas en el desarrollo de scripts ### Bloques del Laboratorio El laboratorio se estructura en cuatro bloques que progresan desde conceptos básicos hasta técnicas avanzadas: 1. **Filtros Fundamentales y Procesamiento de Texto** (80 min): Herramientas básicas de Unix, expresiones regulares y procesamiento con AWK y jq. 2. **Procesamiento Avanzado de Datos** (45 min): Manejo de datos estructurados y generación de reportes. 3. **Monitorización y Análisis del Sistema** (45 min): Herramientas para supervisión de recursos y análisis de logs. 4. **Shell Scripting Avanzado** (80 min): Técnicas avanzadas de programación en shell, incluyendo gestión de secretos y manejo de errores. ## Bloque 1: Filtros Fundamentales y Procesamiento de Texto (80 min) En este bloque exploraremos las herramientas fundamentales para el procesamiento de texto, comenzando con filtros básicos y progresando hacia herramientas más sofisticadas como AWK y jq. Los estudiantes aprenderán a combinar estas herramientas para crear pipelines eficientes de procesamiento de datos. ### 1.1 Introducción y Teoría (20 min) #### Filosofía Unix de Filtros y Pipes - Concepto de filtros: programas que leen de stdin y escriben a stdout - Composición de comandos mediante pipes - Ventajas del diseño modular - Principio de responsabilidad única #### Registros y Campos en la Shell - Registros: Líneas individuales de texto que representan una unidad de información - Campos: Partes de un registro separadas por delimitadores (espacios, tabs, comas, etc.) - Delimitadores comunes: - IFS (Internal Field Separator): Define el separador por defecto (espacio, tab, nueva línea) - Delimitadores personalizados: comas (CSV), tabuladores (TSV), pipes (|), etc. - Acceso a campos: - $1, $2, etc.: Referencias a campos individuales - $0: Línea completa - NF: Número total de campos (en awk) - Delimitador de registro (RS - Record Separator): - Por defecto es el carácter de nueva línea (\n) - Se puede modificar en herramientas como awk usando RS="delimitador" - Permite procesar registros que ocupan múltiples líneas - Ejemplos comunes: - Párrafos (RS="\n\n") - Registros XML (RS="</record>") - Bloques delimitados por marcadores específicos #### Filtros de Ordenación y Unicidad 1. **sort**: Ordenación versátil de texto ```bash # Ordenación básica sort archivo.txt # Ordenación numérica sort -n numeros.txt # Ordenación por múltiples campos sort -k2,2n -k3,3r datos.txt # -k START,END[tipo]: START es el campo inicial, END el campo final # tipo: n=numérico, r=reverso, etc. # Ej: -k2,2n ordena campo 2 numéricamente # Se pueden encadenar múltiples -k para ordenar por varios campos # Ordenación considerando el locale LC_ALL=es_ES.UTF-8 sort -d nombres.txt ``` **Ejercicios** - Ordena por orden alfabético las líneas del archivo `lorem.txt`. Hazlo también en orden inverso - Ordena por orden numérico las líneas del archivo `integers.txt`. - Ordena las líneas de `/etc/passwd` utilizando el UID como clave. Usa -t':' para especificar los dos puntos como delimitador de campo y -k3,3n para ordenar numéricamente por el tercer campo (UID): ```bash sort -t':' -k3,3n /etc/passwd ``` 2. **uniq**: Manejo de líneas duplicadas ```bash # Eliminar duplicados consecutivos sort datos.txt | uniq # Contar ocurrencias sort datos.txt | uniq -c # Mostrar solo duplicados sort datos.txt | uniq -d ``` **Ejercicios** - Mostrar solo datos duplicados del archivo `ips.txt` 3. **paste**: Combinación de archivos ```bash # Unir archivos lado a lado paste nombres.txt telefonos.txt # Usar delimitadores personalizados paste -d',' archivo1.txt archivo2.txt ``` ### 1.2 Filtros de Transformación y Búsqueda (20 min) #### Manipulación de Texto 1. **cut**: Extracción de campos ```bash # Extraer por delimitador cut -d',' -f1,3 datos.csv # Extraer por posición de caracteres cut -c1-10 archivo.txt ``` 2. **sed**: Editor de flujo ```bash # Sustitución básica sed 's/antiguo/nuevo/' archivo.txt # Sustitución global en línea sed 's/antiguo/nuevo/g' archivo.txt # Operaciones múltiples sed -e 's/uno/1/g' -e 's/dos/2/g' archivo.txt ``` 3. **tr**: Transformación de caracteres ```bash # Convertir mayúsculas a minúsculas tr '[:upper:]' '[:lower:]' < archivo.txt # Eliminar caracteres específicos tr -d '\r' < archivo.dos > archivo.unix ``` #### Expresiones Regulares y grep Las expresiones regulares (regex) son patrones de búsqueda que permiten encontrar y manipular texto de forma flexible y potente. Son una herramienta fundamental en el procesamiento de texto y se utilizan en múltiples herramientas como grep, sed, awk, y lenguajes de programación. ##### Elementos Básicos - **Caracteres Literales**: Coinciden consigo mismos - `casa` coincide exactamente con "casa" - **Metacaracteres**: Caracteres especiales con significado - `.` : Cualquier carácter excepto nueva línea - `^` : Inicio de línea - `$` : Fin de línea - `*` : Cero o más repeticiones - `+` : Una o más repeticiones - `?` : Cero o una repetición - `\` : Escape de metacaracteres ##### Clases de Caracteres - **Conjuntos**: `[...]` Define un conjunto de caracteres - `[aeiou]` : Cualquier vocal - `[0-9]` : Cualquier dígito - `[A-Za-z]` : Cualquier letra - `[^0-9]` : Cualquier carácter que NO sea un dígito ##### Cuantificadores - `{n}` : Exactamente n veces - `{n,}` : n o más veces - `{n,m}` : Entre n y m veces - `*` : 0 o más veces (equivalente a {0,}) - `+` : 1 o más veces (equivalente a {1,}) - `?` : 0 o 1 vez (equivalente a {0,1}) ##### Ejemplos Prácticos 1. **Búsqueda de DNIs**: ```regex ^[0-9]{8}[A-Z]$ ``` Encuentra DNIs españoles (8 dígitos + letra mayúscula) 2. **Direcciones Email**: ```regex ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ``` Patrón básico para emails 3. **Números de Teléfono Españoles**: ```regex ^[6789][0-9]{8}$ ``` Móviles y fijos españoles 4. **Direcciones IP**: ```regex ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ``` Formato básico de IPv4 ##### Uso con grep ```bash # Buscar líneas que empiezan con número grep '^[0-9]' archivo.txt # Encontrar DNIs en un archivo grep -E '^[0-9]{8}[A-Z]$' datos.txt # Buscar emails grep -E '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' contactos.txt # Ignorar mayúsculas/minúsculas grep -i 'patron' archivo.txt ``` ##### Consejos Prácticos 1. **Escapado de Caracteres**: - Usar `\` para caracteres especiales - Ejemplo: `\.` para buscar un punto literal 2. **Agrupación**: - `(...)` para agrupar expresiones - Útil para repeticiones y alternativas 3. **Alternativas**: - `|` para especificar alternativas - Ejemplo: `gato|perro` coincide con "gato" o "perro" 4. **Límites de Palabra**: - `\b` marca límite de palabra - Ejemplo: `\bpala\b` encuentra "pala" pero no "palabra" ```bash # Búsqueda básica grep 'patrón' archivo.txt # Contexto alrededor de coincidencias grep -C2 'error' log.txt # Expresiones regulares extendidas grep -E '^[0-9]{3}-[0-9]{2}-[0-9]{4}' datos.txt ``` ### 1.3 AWK para Procesamiento de Texto (25 min) #### Conceptos Básicos de AWK ```awk # Estructura básica awk 'BEGIN { inicialización } /patrón/ { acción } END { finalización }' # Variables especiales # $0 - línea completa # $1, $2, ... - campos # NR - número de registro (línea) # NF - número de campos # FS - separador de campo (input) # OFS - separador de campo (output) ``` #### Ejemplos Prácticos ```awk # Suma de una columna awk '{ sum += $3 } END { print "Total:", sum }' datos.txt # Procesamiento condicional awk '$3 > 1000 { print $1, $2 }' ventas.txt # Estadísticas básicas awk ' { sum += $1; count++ } END { print "Promedio:", sum/count print "Total líneas:", count } ' numeros.txt ``` ### 1.4 Procesamiento JSON con jq (15 min) #### Operaciones con jq Los siguientes ejemplos utilizan los archivos generados en la carpeta `samples/`: - `registros.json`: Colección de registros - `simple-record.json`: Ejemplo de un único registro ##### Consultas Básicas ```bash # Ver estructura de un registro jq '.' samples/simple-record.json # Extraer un campo específico jq '.nombre_completo' samples/simple-record.json # Extraer múltiples campos jq '{nombre: .nombre_completo, edad: .edad}' samples/simple-record.json # Contar número de registros jq 'length' samples/registros.json ``` ##### Filtrado y Búsqueda ```bash # Personas mayores de 30 años jq '.[] | select(.edad > 30)' samples/registros.json # Registros activos con puntuación alta jq '.[] | select(.activo == true and .puntuacion > 8)' samples/registros.json # Buscar por preferencia específica jq '.[] | select(.preferencias | contains(["deportes"]))' samples/registros.json # Personas con más de 3 preferencias jq '.[] | select(.preferencias | length > 3)' samples/registros.json ``` ##### Transformaciones ```bash # Extraer solo DNI y nombre jq '.[] | {dni, nombre: .nombre_completo}' samples/registros.json # Convertir a formato CSV (DNI,Nombre,Edad) jq -r '.[] | [.dni, .nombre_completo, .edad] | @csv' samples/registros.json # Agrupar por rango de edad jq 'group_by(.edad | . / 10 | floor * 10) | map({ rango: "\\(.[0].edad / 10 | floor * 10)-\\(.[0].edad / 10 | floor * 10 + 9)", cantidad: length })' samples/registros.json ``` ##### Estadísticas ```bash # Calcular promedio de edad jq '[.[].edad] | add/length' samples/registros.json # Distribución de preferencias jq '[.[].preferencias[]] | reduce .[] as $pref ({}; .[$pref] += 1)' samples/registros.json # Estadísticas de puntuación jq '[.[].puntuacion] | { promedio: (add/length), min: min, max: max }' samples/registros.json ``` ##### Filtros Compuestos ```bash # Personas activas, mayores de 25, con puntuación > 7 jq '.[] | select(.activo and .edad > 25 and .puntuacion > 7) | {nombre: .nombre_completo, edad, puntuacion}' samples/registros.json # Top 5 personas con mayor puntuación jq 'sort_by(.puntuacion) | reverse | .[0:5] | map({nombre: .nombre_completo, puntuacion})' samples/registros.json # Agrupar por estado activo/inactivo con estadísticas jq 'group_by(.activo) | map({ estado: if .[0].activo then "activo" else "inactivo" end, cantidad: length, edad_promedio: ([.[].edad] | add/length), puntuacion_promedio: ([.[].puntuacion] | add/length) })' samples/registros.json ``` ##### Manipulación de Datos ```bash # Añadir campo calculado jq '.[] | . + { categoria: if .puntuacion >= 8 then "alta" elif .puntuacion >= 5 then "media" else "baja" end }' samples/registros.json # Formatear teléfonos con separadores jq '.[] | . + { telefono_formato: .telefono | "\(.[0:3])-\(.[3:5])-\(.[5:7])-\(.[7:9])" }' samples/registros.json # Extraer apellidos del nombre completo jq '.[] | . + { apellidos: (.nombre_completo | split(", ")[0]) }' samples/registros.json ``` #### Construcción de JSON La construcción de JSON en shell scripts se puede realizar de varias maneras, pero el uso de `jq` ofrece ventajas significativas sobre métodos más simples como `echo`. A continuación, exploraremos las diferentes técnicas y sus implicaciones. ##### Métodos de Construcción JSON Existen dos métodos principales para crear JSON desde la línea de comandos: 1. **Usando echo (NO RECOMENDADO)**: ```bash echo "{\"nombre\": \"$NOMBRE\"}" ``` 2. **Usando jq (RECOMENDADO)**: ```bash jq -n --arg nombre "$NOMBRE" '{"nombre": $nombre}' ``` ##### Ventajas de usar jq -n con --arg 1. **Seguridad**: - Previene inyección de JSON malicioso - Escapa automáticamente caracteres especiales - Maneja correctamente valores nulos y tipos de datos 2. **Validación**: - Garantiza que el JSON generado es válido - Detecta errores de sintaxis en tiempo de ejecución - Formatea el JSON de manera consistente ##### Diferencias entre --arg y --argjson La herramienta jq proporciona dos formas principales de pasar argumentos: - **--arg**: Siempre trata el valor como string - **--argjson**: Interpreta el valor como JSON (números, booleanos, null, arrays, objetos) Veamos ejemplos de cada tipo de dato: 1. **Números**: ```bash EDAD=25 # Con --arg (edad como string): jq -n --arg edad "$EDAD" '{"edad": $edad}' # Salida: {"edad": "25"} # Con --argjson (edad como número): jq -n --argjson edad "$EDAD" '{"edad": $edad}' # Salida: {"edad": 25} ``` 2. **Booleanos**: ```bash ACTIVO=true # Con --arg (booleano como string): jq -n --arg activo "$ACTIVO" '{"activo": $activo}' # Salida: {"activo": "true"} # Con --argjson (booleano real): jq -n --argjson activo "$ACTIVO" '{"activo": $activo}' # Salida: {"activo": true} ``` 3. **Arrays**: ```bash NUMEROS='[1,2,3]' # Con --arg (array como string): jq -n --arg nums "$NUMEROS" '{"números": $nums}' # Salida: {"números": "[1,2,3]"} # Con --argjson (array real): jq -n --argjson nums "$NUMEROS" '{"números": $nums}' # Salida: {"números": [1,2,3]} ``` 4. **Null**: ```bash VALOR="null" # Con --arg (null como string): jq -n --arg val "$VALOR" '{"valor": $val}' # Salida: {"valor": "null"} # Con --argjson (null real): jq -n --argjson val "$VALOR" '{"valor": $val}' # Salida: {"valor": null} ``` ##### Manejo de Caracteres Especiales jq maneja automáticamente los caracteres especiales y el escapado: ```bash DESCRIPCION="línea 1\nlínea 2" # Con echo (problemas con \n): echo "{\"descripcion\": \"$DESCRIPCION\"}" # Con jq (maneja correctamente los saltos de línea): jq -n --arg desc "$DESCRIPCION" '{"descripcion": $desc}' ``` ##### Ejemplos Avanzados de Construcción JSON 1. **Objetos Anidados**: ```bash # Crear objeto con múltiples niveles jq -n --arg user "$USER" --arg home "$HOME" '{ "usuario": { "nombre": $user, "directorio": $home } }' ``` 2. **Listas de Objetos**: Hay varias formas de crear listas de objetos, cada una con sus ventajas: a. **Método Directo**: ```bash jq -n --arg nombre1 "Juan" --arg edad1 "30" \ --arg nombre2 "Ana" --arg edad2 "25" \ '[ {"nombre": $nombre1, "edad": $edad1}, {"nombre": $nombre2, "edad": $edad2} ]' ``` b. **Usando Arrays Split**: ```bash # Útil cuando los datos vienen como strings separados jq -n --arg nombres "Juan Ana María" --arg edades "30 25 28" ' ($nombres | split(" ")) as $n | ($edades | split(" ")) as $e | [ range(0; $n | length) as $i | { "nombre": $n[$i], "edad": $e[$i] } ]' ``` c. **Usando Bucle Bash**: ```bash # Útil para construcción dinámica nombres=("Juan" "Ana" "María") edades=(30 25 28) json_array=$(jq -n '[]') for i in "${!nombres[@]}"; do json_array=$(echo "$json_array" | jq --arg nombre "${nombres[$i]}" \ --arg edad "${edades[$i]}" \ '. + [{"nombre": $nombre, "edad": $edad}]') done echo "$json_array" ``` 3. **Combinación de Archivos JSON**: ```bash # Combinar dos archivos JSON jq -s '.[0] * .[1]' archivo1.json archivo2.json ``` ### Ejercicio Práctico Integrador (15 min) Desarrollar un pipeline que: 1. Procese un log de servidor web 2. Extraiga campos específicos 3. Realice agregaciones 4. Genere un reporte en JSON ```bash # Ejemplo de solución cat access.log | \ awk '{print $1,$9,$10}' | \ sort | \ uniq -c | \ awk '{ print "{\"ip\":\""$2"\",\"status\":"$3",\"bytes\":"$4",\"count\":"$1"}"}' | \ jq -s '{ "accesos": . }' ``` ## Bloque 2: Procesamiento Avanzado de Datos (45 min) Este bloque profundiza en el manejo y transformación de datos estructurados, combinando diferentes herramientas de procesamiento para crear pipelines eficientes. Se pondrá especial énfasis en AWK para el análisis de datos tabulares y la generación de reportes personalizados. ### 2.1 Procesamiento de Datos Tabulares (15 min) #### Formato CSV y TSV - Estructura y convenciones - Manejo de delimitadores especiales - Procesamiento de encabezados - Ejemplos prácticos con AWK: ```awk # Convertir CSV a formato tabular awk -F',' '{ printf "%-20s %-10s %-5s\n", $1, $2, $3 }' datos.csv # Calcular totales por columna awk -F',' 'NR>1 {sum+=$3} END {print "Total:", sum}' ventas.csv ``` ##### Transformación de Formatos - Conversión entre formatos (CSV ↔ JSON ↔ XML) - Normalización de datos - Manejo de caracteres especiales - Validación de estructura ### 2.2 Generación de Reportes (15 min) #### Reportes Estadísticos - Cálculo de métricas básicas (media, mediana, moda) - Agregaciones por grupo - Formateo de salida - Ejemplo: ```awk # Reporte de ventas por región awk -F',' ' NR>1 { ventas[$1] += $2 count[$1]++ } END { print "Región\tTotal\tPromedio" for (region in ventas) { printf "%s\t%.2f\t%.2f\n", region, ventas[region], ventas[region]/count[region] } } ' datos.csv ``` ##### Visualización de Datos - Histogramas en ASCII - Gráficos de barras simples - Tablas formateadas - Ejemplo de histograma: ```awk # Histograma de frecuencias awk '{ count[$1]++ if(max < count[$1]) max = count[$1] } END { for (item in count) { printf "%-10s |", item for (i=0; i<count[item]; i++) printf "#" print " (" count[item] ")" } }' datos.txt ``` ### 2.3 Integración de Herramientas (15 min) #### Pipelines Avanzados - Combinación de múltiples herramientas - Procesamiento paralelo con xargs - Manejo de errores en pipelines - Ejemplo integrador: ```bash # Pipeline para análisis de logs cat access.log | awk '{print $1,$9,$7}' | sort | uniq -c | awk '$1>100' | jq -R 'split(" ")|{count:.[0],ip:.[1],status:.[2],path:.[3]}' | jq -s 'group_by(.status)|map({status:.[0].status,count:length})' ``` ##### Casos de Uso Prácticos - Análisis de logs de servidor - Procesamiento de datos financieros - Generación de reportes de rendimiento - Ejemplo de reporte de rendimiento: ```bash # Análisis de tiempos de respuesta awk ' /response_time/ { times[$1] = $3 count[$1]++ total[$1] += $3 if($3 > max[$1]) max[$1] = $3 if(!min[$1] || $3 < min[$1]) min[$1] = $3 } END { print "Endpoint\tAvg\tMin\tMax\tCount" for (ep in times) { printf "%s\t%.2f\t%.2f\t%.2f\t%d\n", ep, total[ep]/count[ep], min[ep], max[ep], count[ep] } } ' server.log ``` #### Ejercicios Prácticos 1. Crear un pipeline que procese datos de ventas y genere un reporte por región y producto 2. Implementar un analizador de logs que identifique patrones de error y genere alertas 3. Desarrollar un script que transforme datos entre diferentes formatos manteniendo la integridad ## Bloque 3: Monitorización y Análisis del Sistema (45 min) Este bloque se centra en técnicas y herramientas para la monitorización efectiva de recursos del sistema y análisis de logs. Los estudiantes aprenderán a utilizar herramientas estándar de Unix/Linux para supervisar el rendimiento del sistema y diagnosticar problemas. ### 3.1 Monitorización de Recursos del Sistema (20 min) > **Nota sobre herramientas de monitorización** > > Algunas herramientas requieren instalación previa. Aquí se muestra cómo instalarlas según la distribución: > > En Debian/Ubuntu: > ```bash > # Herramientas básicas de monitorización > sudo apt install sysstat # Incluye: sar, iostat, mpstat > sudo apt install iotop # Monitor de I/O > sudo apt install iftop # Monitor de tráfico de red > sudo apt install nethogs # Monitor de ancho de banda por proceso > ``` > > En Red Hat/CentOS: > ```bash > sudo yum install sysstat > sudo yum install iotop > sudo yum install iftop > sudo yum install nethogs > ``` > > Herramientas que suelen estar disponibles por defecto: > - top, htop > - ps, free > - df, du > - netstat (o ss) > - vmstat #### Monitorización de CPU Comandos para monitorizar el uso de CPU: 1. **top -b -n 1**: Muestra una instantánea del estado del sistema - `-b`: Modo batch (no interactivo) - `-n 1`: Número de iteraciones (una sola vez) Ejemplo de salida: ``` top - 14:23:36 up 3 days, 23:42, 1 user, load average: 0.52, 0.58, 0.59 Tasks: 180 total, 1 running, 179 sleeping, 0 stopped, 0 zombie %Cpu(s): 5.9 us, 3.1 sy, 0.0 ni, 90.6 id, 0.3 wa, 0.0 hi, 0.1 si MiB Mem: 15951.3 total, 9049.5 free, 4584.7 used, 2317.1 buff/cache ``` - Primera línea: Uptime y carga promedio - Segunda línea: Resumen de procesos - Tercera línea: Uso de CPU (us: usuario, sy: sistema, id: idle) - Cuarta línea: Estado de la memoria 2. **vmstat 1**: Estadísticas de memoria virtual y CPU - `1`: Actualiza cada segundo Ejemplo de salida: ``` procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 9049492 543008 1774084 0 0 0 3 1 2 6 3 91 0 0 ``` - procs: r (ejecutando), b (bloqueados) - memory: swpd (memoria swap), free (memoria libre) - swap: si (swap in), so (swap out) - io: bi (bloques in), bo (bloques out) - cpu: us (usuario), sy (sistema), id (idle), wa (wait) 3. **mpstat -P ALL 1**: Estadísticas por CPU - `-P ALL`: Muestra todos los procesadores - `1`: Actualiza cada segundo Ejemplo de salida: ``` Linux 5.15.0 (hostname) 14:23:36 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle # Donde: # CPU: Número de CPU (all para el total, 0,1,etc para cada procesador) # %usr: Porcentaje de tiempo en modo usuario (aplicaciones) # %nice: Porcentaje de tiempo en modo usuario con prioridad nice # %sys: Porcentaje de tiempo en modo kernel (sistema) # %iowait: Porcentaje de tiempo esperando por operaciones I/O # %irq: Porcentaje de tiempo manejando interrupciones hardware # %soft: Porcentaje de tiempo manejando interrupciones software # %steal: Porcentaje de tiempo en espera involuntaria (virtualización) # %guest: Porcentaje de tiempo ejecutando máquina virtual # %idle: Porcentaje de tiempo en que la CPU está inactiva 14:23:36 all 5.89 0.00 3.11 0.31 0.00 0.10 0.00 0.00 90.59 14:23:36 0 6.12 0.00 3.06 0.41 0.00 0.20 0.00 0.00 90.21 14:23:36 1 5.67 0.00 3.16 0.20 0.00 0.00 0.00 0.00 90.97 ``` - Muestra porcentajes de tiempo en diferentes estados para cada CPU - Útil para detectar desbalances entre cores 4. **ps aux --sort=-%cpu**: Procesos ordenados por CPU - `aux`: Formato detallado - `--sort=-%cpu`: Ordena por CPU descendente Ejemplo de salida: ``` USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND user1 12345 8.5 2.1 654208 174504 ? Sl 13:25 0:45 firefox user1 23456 6.2 1.5 542168 123456 ? Rl 14:01 0:32 chrome ``` - %CPU: Porcentaje de CPU usado - %MEM: Porcentaje de memoria - VSZ: Memoria virtual - RSS: Memoria física real 5. **uptime**: Tiempo de funcionamiento y carga Ejemplo de salida: ``` 14:23:36 up 3 days, 23:42, 1 user, load average: 0.52, 0.58, 0.59 ``` - Tiempo actual - Tiempo de funcionamiento - Número de usuarios - Carga promedio (1, 5, 15 minutos) 6. **cat /proc/loadavg**: Carga del sistema en formato raw Ejemplo de salida: ``` 0.52 0.58 0.59 1/780 12345 ``` - Tres primeros números: carga promedio 1, 5, 15 minutos - 1/780: Procesos ejecutándose/total - 12345: Último PID asignado ##### Monitorización de Memoria Comandos para monitorizar el uso de memoria: - `free -h`: Muestra uso de memoria y swap - `-h`: Formato human-readable (GB, MB, etc.) - `vmstat -s`: Estadísticas detalladas de memoria virtual - `-s`: Muestra estadísticas en modo tabla - `ps aux --sort=-%mem`: Lista procesos por uso de memoria - `--sort=-%mem`: Ordena por uso de memoria descendente - `top -b -n 1 -o %MEM`: Procesos ordenados por uso de memoria - `-o %MEM`: Ordena por porcentaje de memoria - `pmap -x PID`: Mapa de memoria de un proceso - `-x`: Formato extendido con detalles - Muestra RSS, tamaño y permisos por región ```bash # Visión general del uso de memoria free -h vmstat -s # Uso de memoria por proceso (incluye RSS) ps aux --sort=-%mem | head -n 10 top -b -n 1 -o %MEM # Memoria detallada de un proceso específico pmap -x PID cat /proc/PID/status ``` ##### Monitorización de Disco Comandos para monitorizar almacenamiento: - `df -h`: Uso de espacio en sistemas de archivos - `-h`: Formato human-readable - `-i`: Muestra información de inodos - `du -sh`: Uso de espacio por directorio - `-s`: Summarize (total por directorio) - `-h`: Formato human-readable - `iostat -x 1`: Estadísticas de E/S - `-x`: Modo extendido con más métricas - `1`: Actualiza cada segundo - `iotop -b -n 1`: Monitor de E/S por proceso - `-b`: Modo batch - `-n 1`: Una iteración ```bash # Espacio en disco df -h df -i # Inodos # Uso de disco por directorio du -sh /* du -sh /var/log/* # I/O en tiempo real iostat -x 1 iotop -b -n 1 ``` ##### Monitorización de Red Comandos básicos para monitorizar red (disponibles por defecto): 1. **netstat -i**: Estado de interfaces - `-i`: Muestra estadísticas de interfaz Ejemplo de salida: ``` Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg eth0 1500 158320 0 0 0 108959 0 0 0 BMRU lo 65536 8166 0 0 0 8166 0 0 0 LRU ``` - MTU: Unidad máxima de transmisión - RX: Paquetes recibidos (OK, errores, descartados, overflow) - TX: Paquetes transmitidos (OK, errores, descartados, overflow) - Flg: Flags de la interfaz (B:broadcast, M:multicast, R:running, U:up) 2. **netstat -tuln**: Puertos en escucha - `-t`: TCP - `-u`: UDP - `-l`: Solo puertos en escucha - `-n`: No resolver nombres Ejemplo de salida: ``` Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp6 0 0 :::80 :::* LISTEN udp 0 0 127.0.0.1:53 0.0.0.0:* ``` - Proto: Protocolo (tcp/udp) - Recv-Q/Send-Q: Bytes en cola de recepción/envío - Local/Foreign Address: Direcciones y puertos locales/remotos - State: Estado de la conexión (para TCP) 3. **ss**: Versión moderna de netstat (reemplazo recomendado) ```bash # Mostrar todas las conexiones TCP ss -ta # Mostrar estadísticas ss -s ``` Ejemplo de salida ss -s: ``` Total: 584 TCP: 47 (estab 36, closed 0, orphaned 0, timewait 0) Transport Total IP IPv6 RAW 0 0 0 UDP 17 12 5 TCP 47 36 11 ``` Herramientas avanzadas (requieren instalación): 4. **iftop** (opcional): Monitor de tráfico en tiempo real ```bash # Instalar: sudo apt install iftop iftop -n # -n: no resolver nombres ``` 5. **nethogs** (opcional): Monitor de ancho de banda por proceso ```bash # Instalar: sudo apt install nethogs nethogs eth0 ``` Comandos básicos para diagnóstico de red: ```bash # Ver interfaces y su estado ip link show # Ver direcciones IP ip addr show # Ver tabla de rutas ip route show # Estadísticas de interfaces netstat -i # Puertos en escucha netstat -tuln # o ss -tuln ``` #### 3.2 Análisis de Logs del Sistema (15 min) ##### Logs Críticos del Sistema ```bash # Mensajes del sistema tail -f /var/log/syslog tail -f /var/log/messages # Logs de autenticación tail -f /var/log/auth.log # Logs del kernel dmesg | tail journalctl -k -f ``` ##### Filtrado Básico de Logs ```bash # Buscar errores grep -i error /var/log/syslog grep -i failed /var/log/auth.log # Eventos recientes journalctl -n 50 --no-pager ``` #### 3.3 Scripts de Monitorización (10 min) ##### Script Básico de Monitorización ```bash #!/bin/bash # Monitor de recursos básico # CPU echo "=== CPU ===" top -b -n 1 | head -n 3 # Memoria echo -e "\n=== Memoria ===" free -h | head -n 2 # Disco echo -e "\n=== Disco ===" df -h | head -n 6 # Red echo -e "\n=== Red ===" netstat -i | head -n 3 ``` ##### Variables de Entorno para Monitorización ```bash # Configuración de umbrales DISK_THRESHOLD=${DISK_THRESHOLD:-90} MEM_THRESHOLD=${MEM_THRESHOLD:-80} CPU_THRESHOLD=${CPU_THRESHOLD:-75} ``` #### Ejercicios Prácticos 1. Crear un script que monitorice y alerte cuando el uso de CPU supere un umbral 2. Implementar un monitor de espacio en disco que identifique directorios de mayor crecimiento 3. Desarrollar un script que recopile estadísticas de red y genere un reporte horario ## Bloque 4: Shell Scripting Avanzado (80 min) Este bloque final integra conceptos avanzados de shell scripting, con especial énfasis en el manejo de arrays, expansión de variables y gestión segura de secretos. Los estudiantes aprenderán a crear scripts robustos y seguros utilizando las características avanzadas de la shell. ### 4.1 Fundamentos de Arrays y Variables (25 min) #### Arrays en Bash 1. **Declaración y Asignación** ```bash # Array indexado frutas=("manzana" "pera" "naranja") # Array asociativo declare -A usuarios usuarios[admin]="root" usuarios[web]="www-data" ``` 2. **Operaciones con Arrays** ```bash # Añadir elementos frutas+=("plátano") # Acceder a elementos echo "${frutas[0]}" # Primer elemento echo "${frutas[-1]}" # Último elemento echo "${frutas[@]}" # Todos los elementos echo "${#frutas[@]}" # Número de elementos echo "${!frutas[@]}" # Índices del array # Slicing echo "${frutas[@]:1:2}" # 2 elementos desde índice 1 ``` 3. **Iteración sobre Arrays** ```bash # Iterar sobre valores for fruta in "${frutas[@]}"; do echo "Procesando: $fruta" done # Iterar sobre índices for i in "${!frutas[@]}"; do echo "Índice $i: ${frutas[$i]}" done ``` ##### Expansión de Variables Avanzada 1. **Valores por Defecto** ```bash # Si variable no existe o está vacía echo "${PUERTO:-8080}" # Usa 8080 como valor por defecto echo "${USUARIO:=anonymous}" # Asigna y usa valor por defecto # Manejo de errores echo "${CONFIG:?'Archivo de configuración no especificado'}" # Valor alternativo si existe echo "${DEBUG:+'-v'}" # Usa -v solo si DEBUG está definida ``` 2. **Manipulación de Strings** ```bash # Substrings texto="Hola Mundo" echo "${texto:0:4}" # "Hola" echo "${texto:5}" # "Mundo" # Patrones archivo="script.test.sh" echo "${archivo#*.}" # "test.sh" (elimina hasta primer punto) echo "${archivo##*.}" # "sh" (elimina hasta último punto) echo "${archivo%.*}" # "script.test" (elimina desde último punto) echo "${archivo%%.*}" # "script" (elimina desde primer punto) ``` 3. **Sustitución de Patrones** ```bash # Reemplazar primera ocurrencia echo "${texto/Mundo/Amigo}" # "Hola Amigo" # Reemplazar todas las ocurrencias echo "${texto//o/0}" # "H0la Mund0" # Mayúsculas/minúsculas echo "${texto^^}" # "HOLA MUNDO" echo "${texto,,}" # "hola mundo" ``` ### 4.2 Gestión de Secretos y Variables de Entorno (20 min) #### Codificación y Decodificación base64 ```bash # Codificar secretos SECRET=$(echo -n "mi_contraseña_secreta" | base64) echo "Secreto codificado: $SECRET" # Decodificar echo "$SECRET" | base64 -d # Manejo de múltiples líneas CONFIG=$(cat <<EOF | base64 usuario=admin password=secreto host=localhost EOF ) ``` #### Cifrado de Secretos con OpenSSL 1. **Cifrado Simétrico Básico** ```bash # Generar clave segura PASSPHRASE=$(openssl rand -base64 32) # Cifrar un secreto echo "secreto_importante" | openssl enc -aes-256-cbc -salt -pbkdf2 \ -out secreto.enc -pass pass:"$PASSPHRASE" # Descifrar openssl enc -aes-256-cbc -d -salt -pbkdf2 \ -in secreto.enc -pass pass:"$PASSPHRASE" ``` 2. **Gestión de Archivos de Configuración** ```bash #!/bin/bash # Función para cifrar configuración encrypt_config() { local config_file="$1" local key_file="$2" if [[ ! -f "$config_file" ]]; then echo "Error: Archivo de configuración no encontrado" return 1 } openssl enc -aes-256-cbc -salt -pbkdf2 \ -in "$config_file" \ -out "${config_file}.enc" \ -pass file:"$key_file" # Eliminar archivo original de forma segura shred -u "$config_file" } # Función para descifrar configuración decrypt_config() { local encrypted_file="$1" local key_file="$2" local temp_file=$(mktemp) openssl enc -aes-256-cbc -d -salt -pbkdf2 \ -in "$encrypted_file" \ -out "$temp_file" \ -pass file:"$key_file" # Cargar configuración source "$temp_file" # Limpiar shred -u "$temp_file" } ``` #### Mejores Prácticas de Seguridad 1. **Gestión de Variables de Entorno** ```bash # Archivo .env.template (versionar esto) DB_HOST=localhost DB_PORT=5432 DB_USER= DB_PASS= # Script para cargar variables load_env() { if [[ -f .env ]]; then set -a source .env set +a else echo "Error: Archivo .env no encontrado" exit 1 fi } ``` 2. **Validación de Secretos** ```bash validate_secrets() { local required_vars=("DB_USER" "DB_PASS" "API_KEY") local missing=0 for var in "${required_vars[@]}"; do if [[ -z "${!var}" ]]; then echo "Error: Variable $var no definida" missing=1 fi done return $missing } ``` ### 4.3 Funciones y Modularización (20 min) #### Diseño de Funciones ```bash # Función con validación de argumentos get_user_data() { local user_id="$1" local field="${2:-name}" # valor por defecto if [[ -z "$user_id" ]]; then echo "Error: user_id requerido" >&2 return 1 fi # Usar variable local para el resultado local result result=$(grep "^$user_id:" /etc/passwd) case "$field" in name) echo "$result" | cut -d: -f1 ;; home) echo "$result" | cut -d: -f6 ;; shell) echo "$result" | cut -d: -f7 ;; *) echo "Campo no válido: $field" >&2; return 1 ;; esac } ``` #### Bibliotecas de Funciones ```bash # lib/logger.sh log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case "$level" in INFO|WARNING|ERROR|DEBUG) echo "[$timestamp] $level: $message" >&2 ;; *) echo "Nivel de log no válido: $level" >&2 return 1 ;; esac } # lib/config.sh load_config() { local config_file="$1" if [[ ! -f "$config_file" ]]; then log ERROR "Archivo de configuración no encontrado: $config_file" return 1 fi source "$config_file" } ``` ### 4.4 Uso de exec (15 min) #### Fundamentos de exec `exec` es una característica poderosa en bash que permite: - Reemplazar el proceso actual con un nuevo proceso - Redirigir descriptores de archivo - Ejecutar comandos con mayor eficiencia 1. **Reemplazo de Proceso** ```bash # El script termina aquí, reemplazado por date exec date echo "Esto nunca se ejecuta" # Útil para wrappers y proxies #!/bin/bash # wrapper.sh setup_environment() { export PATH="/custom/path:$PATH" export CUSTOM_VAR="value" } setup_environment exec "$@" # Reemplaza el wrapper con el comando proporcionado ``` 2. **Redirección de Descriptores de Archivo** ```bash # Redirigir stdout a un archivo para todo el script exec 1>log.txt # Redirigir stderr a stdout exec 2>&1 # Crear nuevo descriptor de archivo para lectura exec 3<input.txt read -u 3 line # Crear nuevo descriptor de archivo para escritura exec 4>output.txt echo "log entry" >&4 # Cerrar un descriptor de archivo exec 4>&- ``` 3. **Uso en Scripts de Logging** ```bash #!/bin/bash # Configurar logging setup_logging() { local log_file="$1" # Guardar stdout original exec 3>&1 # Redirigir stdout y stderr al archivo de log exec 1>"$log_file" 2>&1 # Ahora podemos usar fd 3 para output real echo "Starting script" >&3 } # Restaurar stdout cleanup_logging() { # Restaurar stdout y stderr exec 1>&3 2>&1 # Cerrar fd temporal exec 3>&- } ``` 4. **Patrones Comunes** ```bash # Ejecutar en nuevo shell sin afectar el actual ( # Estas redirecciones solo afectan al subshell exec 1>log.txt 2>error.log ./script.sh ) # Capturar tanto stdout como stderr separadamente { exec 3>&1 # Guardar stdout actual exec 4>&2 # Guardar stderr actual exec 1>stdout.log # Nuevo stdout exec 2>stderr.log # Nuevo stderr # Código aquí... exec 1>&3 # Restaurar stdout exec 2>&4 # Restaurar stderr exec 3>&- # Cerrar fd temporal exec 4>&- # Cerrar fd temporal } ``` 5. **Mejores Prácticas** ```bash #!/bin/bash # Declarar descriptores de archivo como readonly declare -r LOG_FD=3 declare -r ERR_FD=4 # Función para inicializar descriptores setup_fd() { # Verificar que los fd no estén en uso if [ -e /dev/fd/$LOG_FD ] || [ -e /dev/fd/$ERR_FD ]; then echo "Error: Descriptores de archivo ya en uso" >&2 exit 1 fi # Abrir descriptores exec "$LOG_FD">app.log exec "$ERR_FD">error.log } # Función para limpiar descriptores cleanup_fd() { # Cerrar descriptores abiertos exec "$LOG_FD">&- exec "$ERR_FD">&- } # Asegurar limpieza al salir trap cleanup_fd EXIT ``` #### Casos de Uso Avanzados 1. **Daemon Script** ```bash #!/bin/bash daemonize() { # Redirigir descriptores estándar exec 1>/dev/null exec 2>/dev/null exec 0</dev/null # Ejecutar en background ( # Desacoplar del terminal cd / umask 0 setsid "$@" & ) } # Uso daemonize ./servicio.sh ``` 2. **Rotación de Logs** ```bash #!/bin/bash rotate_logs() { local log_file="$1" local max_size=$((10*1024*1024)) # 10MB if [[ -f "$log_file" && $(stat -f%z "$log_file") -gt $max_size ]]; then # Cerrar descriptor actual exec 3>&- # Rotar archivo mv "$log_file" "${log_file}.old" # Reabrir descriptor exec 3>"$log_file" fi } ``` ### 4.5 Manejo de Errores y Depuración (15 min) #### Control de Errores ```bash #!/bin/bash set -euo pipefail # Trap para cleanup cleanup() { local exit_code=$? log INFO "Limpiando recursos temporales..." rm -f /tmp/temp.* exit $exit_code } trap cleanup EXIT # Manejo de errores personalizado error_handler() { local line_no=$1 local error_code=$2 log ERROR "Error en línea $line_no (código: $error_code)" } trap 'error_handler ${LINENO} $?' ERR ``` #### Logging y Depuración ```bash # Niveles de debug DEBUG=${DEBUG:-0} debug() { if ((DEBUG >= 1)); then log DEBUG "$*" fi } trace() { if ((DEBUG >= 2)); then log TRACE "$*" fi } # Uso debug "Procesando archivo: $file" trace "Variables: $(set | grep '^[A-Z]')" ``` #### Ejercicios Prácticos 1. Crear una biblioteca de funciones para gestión de configuración que: - Soporte múltiples formatos (env, json) - Valide valores requeridos - Maneje secretos de forma segura 2. Implementar un script que procese un array de usuarios y: - Valide formato de datos - Aplique transformaciones - Genere reportes - Maneje errores apropiadamente 3. Desarrollar un sistema de logging que: - Soporte múltiples niveles - Rote archivos de log - Incluya información de contexto - Permita filtrado por severidad ### Metodología - Cada bloque combina explicación teórica con ejercicios prácticos - Los ejercicios están diseñados para ser incrementales en complejidad - Se proporcionarán datos de ejemplo para las prácticas - Los estudiantes trabajarán con casos de uso realistas - Se fomentará la experimentación y la resolución de problemas ### Requisitos Previos Los estudiantes deben tener: - Conocimientos básicos de línea de comandos Unix/Linux - Experiencia básica en scripting shell - Familiaridad con conceptos básicos de programación - Un entorno Linux/Unix con las herramientas estándar instaladas